2009-06-05 10 views
2

Ich versuche, zwei Stücke von Metadaten (Volumen und Ausgabe) nach Artikeln zu suchen, die entweder ein Volumen, ein Problem oder beides haben. Die Metadaten werden in einer Tabelle mit Artikel-ID, Schlüssel (Metadatenfeld-ID) und Wert gespeichert.Äußere Verbindung mit Bedingungen?

Dies funktioniert, aber es scheint zu komplex und wiederholt:

select volume.text_value as volume_value, issue.text_value as issue_value 
    from metadatavalue item 
    left outer join (select item_id, text_value from metadatavalue 
        where metadata_field_id = 90) volume 
        on item.item_id = volume.item_id 
    left outer join (select item_id, text_value from metadatavalue 
        where metadata_field_id = 91) issue 
        on item.item_id = issue.item_id 
    where item.metadata_field_id in (90, 91) 

Gibt es einen einfacheren Weg, um diese Abfrage zu schreiben?

Danke.

+1

Die Abfrage an erster Stelle zu sein scheint falsch, können Sie die Artikel-ID verwenden, muss in die Joins !! – tekBlues

+0

Schau Mama, kein Self-Joins! Überprüfen Sie meine Lösung unten. ;) – tom

+0

Ich habe die Element-ID in den Joins verwendet. Was ich vergessen habe, war eine UNTERSCHEIDUNG. –

Antwort

2

PostgreSQL unterstützt eine vollständige äußere Verknüpfung, die die Abfrage vereinfachen kann:

select v.text_value as volume_value, i.text_value as issue_value 
     from (select item_id, text_value 
       from metadatavalue 
       where metadata_field_id = 90) v 
      full join 
      (select item_id, text_value 
       from metadatavalue 
       where metadata_field_id = 91) i 
      using (item_id) 
+0

Danke! Genau das, was ich gesucht habe. –

+1

Ich kann nicht glauben, dass mir diese hässlichen Unterabfragen nicht in den Sinn gekommen sind. Dies wird einen effizienten MERGE JOIN verwenden, vorausgesetzt es gibt einen Index für (metadata_field_id, item_id). Definitiv ein +1. – Quassnoi

+0

Danke für die Korrekturen und +1. Ich bin mit PostgresSQL nicht so vertraut. Ich habe bemerkt, dass du item_id nicht in die Ergebnisse aufgenommen hast, aber wenn du item_id haben möchtest, musst du die item_ids aus den beiden Tabellen KOHLEN. –

2
SELECT DISTINCT ON (item_id) 
     item_id, 
     CASE metadata_field_id 
     WHEN 90 THEN 
       text_value 
     ELSE (
       SELECT text_value 
       FROM metadatavalue m 
       WHERE m.metadata_field_id = 90 
         AND m.item_id = i.item_id 
       ) 
     END AS volume, 
     CASE metadata_field_id 
     WHEN 91 THEN 
       text_value 
     ELSE (
       SELECT text_value 
       FROM metadatavalue m 
       WHERE m.metadata_field_id = 91 
         AND m.item_id = i.item_id 
       ) 
     END AS issue 
FROM metadatavalue 
WHERE metadata_field_id IN (90, 91) 
ORDER BY 
     item_id 

Mit einem Index auf (item_id, metadata_field) wird diese Abfrage verbessern.

Dies wird besser funktionieren, wenn es items mit metadata von 90 und 91, im Vergleich zur Gesamtzahl der items wenige.

Wenn fast alle items diese metadata haben, benutzen Sie einfach:

SELECT * 
FROM (
     SELECT item_id, 
       (
       SELECT text_value 
       FROM metadatavalue m 
       WHERE m.metadata_field_id = 90 
         AND m.item_id = i.item_id 
       ) volume, 
       (
       SELECT text_value 
       FROM metadatavalue m 
       WHERE m.metadata_field_id = 91 
         AND m.item_id = i.item_id 
       ) issue 
     FROM items 
     ) q 
WHERE issue IS NOT NULL OR volume IS NOT NULL 
+0

Guter Punkt, danke. Die erste Abfrage scheint nur eine Neuerung von mir zu sein, aber besser formatiert :-) Es gibt tatsächlich wenige Artikel mit Volumen/Ausgabe. –

+0

@Nicholas: Ummm ... Ich korrigierte die Abfrage etwas, um Duplikate zu vermeiden. – Quassnoi

+0

@Nicholad: und noch ein wenig, um extra SELECT zu vermeiden, wo sie nicht benötigt werden. – Quassnoi

2

dieses Versuchen

select volume.text_value as volume_value, 
     issue.text_value as issue_value  
from metadatavalue item  
     left outer join metadatavalue volume      
      on item.item_id = volume.item_id  
     left outer join metadatavalue issue      
      on item.item_id = issue.item_id  
where volume.metadata_field_id = 90 
and  issue.metadata_field_id = 91 
+0

Dies funktioniert nicht (es ist ähnlich zu meinem ursprünglichen Versuch bei dieser Abfrage). Es verhält sich so, als wären die Joins innere Joins und nicht äußere Joins. –

+0

Entschuldigung, ich habe Ihre Frage möglicherweise falsch interpretiert. "Gibt es eine einfachere Möglichkeit, diese Abfrage zu schreiben?" ... Ich dachte, es wäre nur eine einfachere Abfrage, die du haben wolltest. –

+0

@Nicholas: Wenn das nicht das ist, was du gefragt hast, dann musst du deine Frage besser formulieren. Sie haben nach einer Vereinfachung Ihrer Anfrage gefragt, und das tut das. – BenAlabaster

0

die Case-Anweisung Unter Verwendung der Werte in Eimern zu trennen, wird die Max-Funktion die text_value Daten ermöglichen nach oben schweben.

select 
    item_id 
    ,max(case metadata_field_id when 90 then text_value else null end) as volume_value 
    ,max(case metadata_field_id when 91 then text_value else null end) as issue_value 
from 
    metadatavalue 
group by 
    item_id 

Hier ist, was ich zu Testzwecken verwendet:

select 
    item_id 
    ,max(case metadata_field_id when 90 then text_value else null end) as volume_value 
    ,max(case metadata_field_id when 91 then text_value else null end) as issue_value 
from 
    (
    select 1 as item_id, 90 as metadata_field_id, '90-I am here' as text_value 
    union 
    select 1 as item_id, 91 as metadata_field_id, '91-Me too' as text_value 
    union 
    select 2 as item_id, 90 as metadata_field_id, null as text_value 
    union 
    select 2 as item_id, 91 as metadata_field_id, '91-funky' as text_value 
    union 
    select 3 as item_id, 90 as metadata_field_id, '90-fresh' as text_value 
) metadatavalue 
group by 
    item_id 

Ergebnisse:

item_id volume_value issue_value 
1  90-I am here 91-Me too 
2  NULL   91-funky 
3  90-fresh  NULL 

Anmerkung: Ich SQL Server verwendet & Test zu modellieren und verändert dann die Syntax Postgres übereinstimmen .

0

Sie das Prädikat aus Ihrem inneren Tabelle in der Join-Bedingung nehmen:

select volume.text_value as volume_value, issue.text_value as issue_value 
from metadatavalue item 
left outer join metadatavalue volume 
       on volume.metadata_field_id = 90 and volume.item_id = item.item_id 
left outer join metadatavalue issue 
       on issue.metadata_field_id = 91 and issue.item_id = item.item_id 
where item.metadata_field_id in (90, 91) 

Obwohl ich nicht sicher bin ich Sinn Ihrer schema-- mache es mir scheint, dass item sollte sich auf eine andere Tabelle beziehen als volume und issue? etw mehr wie:

select item.*, volume.text_value as volume_value, issue.text_value as issue_value 
from item 
left outer join metadatavalue volume 
       on volume.metadata_field_id = 90 and volume.item_id = item.item_id 
left outer join metadatavalue issue 
       on issue.metadata_field_id = 91 and issue.item_id = item.item_id 

Dies macht auch die ziemlich seltsam, wo Zustand weggehen. Sie könnten die obige Abfrage in eine Ansicht einfügen ("item_volume_issue") und es scheint, dass Volume und Issue nur zusätzliche Spalten in item_volume_issue anstelle von separaten Tabellen sind (was für mich sinnvoller ist).

+1

Die WHERE-Bedingung ist erforderlich, um die Abfrage auf die Elemente zu beschränken, die ein Volumen oder Problem haben. (Nicht alle Artikel tun; Ich war ziemlich zweideutig darüber in meiner Frage, tut mir leid.) –

Verwandte Themen