2010-06-09 11 views
5

Ich versuche, eine PostgreSQL-Abfrage zu konstruieren, die das Folgende tut, aber bis jetzt waren meine Bemühungen vergeblich.Zeile aus Tabelle auswählen und ein Feld durch eins aus einer anderen Spalte ersetzen, wenn es existiert

Problem: Es gibt zwei Tabellen: A und B. Ich mag würde alle Spalten aus Tabelle A wählen (mit Spalten: ID, Name, Beschreibung) und ersetzen die „A.name“ Spalte mit der Wert der Spalte "B.title" aus Tabelle B (mit Spalten: id, table_A_id title, langcode), wobei B.table_A_id 5 ist und B.langcode "nl" ist (wenn Zeilen vorhanden sind).

Meine Versuche:

SELECT A.name, 
case when exists(select title from B where table_A_id = 5 and langcode= 'nl') 
then B.title 
else A.name 
END 
FROM A, B 
WHERE A.id = 5 and B.table_A_id = 5 and B.langcode = 'nl' 

-- second try: 
SELECT COALESCE(B.title, A.name) as name 
from A, B 
where A.id = 5 and B.table_A_id = 5 and exists(select title from B where table_A_id = 5 and langcode= 'nl') 

Ich habe versucht, eine CASE und COALESCE() mit gescheitert, aber aufgrund meiner Unerfahrenheit mit beiden Konzepten.

Vielen Dank im Voraus.

+0

Nicht sehr klar. "Wenn nicht null", sagen Sie im Titel. Aber das Wort "null" erscheint nicht in der Erklärung. Du meinst "wenn es eine verwandte Zeile in der anderen Tabelle gibt"? "B.table_A_id ist 5": Das ist wirklich eine Konstante oder verweist auf den entsprechenden A.id-Wert? Ein Beispiel würde helfen – leonbloy

+0

Möchten Sie nur den "B.title" subtrahieren, wenn "A.name" null ist? – rfusca

+0

Ich habe den Titel bearbeitet und ein Beispiel hinzugefügt. Entschuldigung für die Verwirrung. B.table_A_id entspricht tatsächlich A.id. Und bezüglich der Ersetzung sollte A.name durch B.title ersetzt werden, wenn B.title mit den obigen WHERE-Bedingungen existiert. – EarthMind

Antwort

3

araqnid's ist die Antwort, die du suchst, ich wette.

Wenn Sie jedoch erzwingen möchten, dass nicht mehr als eine Zeile für jede ursprüngliche übereinstimmende A Zeile zurückgegeben wird, möchten Sie möglicherweise einen Subselect anstelle eines LINKEN JOINs ausführen. Zum Beispiel:

SELECT A.id, COALESCE(
    (SELECT max(B.title) FROM B WHERE 
    langcode = 'nl' AND B.table_a_id = A.id), A.name) as name 
FROM A 
WHERE A.id = 5 

Ich benutze „max“ hier einen beliebigen Wert zu wählen, für den Fall, es ist mehr als eine. Sie können "min" oder was auch immer Sie in Ihrem Fall als geeignet betrachten.

Vielleicht ist dies einfacher zu verstehen als der LINKE JOIN, aber (abgesehen davon, dass die beiden nicht exakt äquivalent sind), wird ein JOIN besser als N Subselects sein (viel besser ist N groß).

Wie auch immer, aus Lernsicht ist es gut, beides zu verstehen.

+0

Das macht genau das, was ich will. Er ersetzt A.name durch B.title, wenn der zweite existiert, andernfalls verwendet er A.name als Rückgabewert aus der COALESCE-Funktion. Die B-Tabelle wird immer nur 1 Zeile für die gegebene WHERE-Bedingung haben, aber ich frage mich, warum Sie MAX() (oder MIN()) anstelle von LIMIT 1 verwendet haben. Auch aus Neugier, gibt es eine Möglichkeit, die Namensspalte zu ersetzen mit dem Wert aus der COALESCE() - Funktion? Ich benutze A. *, um alle Werte aus Tabelle A zu erhalten und alias das Ergebnis von COALESCE() als Name und fügt einen zusätzlichen Spaltennamen1 in das zurückgegebene Array ein. Danke noch einmal. – EarthMind

+0

Sie können "LIMIT 1" perfekt verwenden (es ist nur ein bisschen mehr Postgresql-zentrisch als MAX/MIN). Ich habe einen Alias ​​("name") hinzugefügt. Ich fürchte, Sie müssen Ihr 'A. *' durch die vollständige Spaltenliste ersetzen (außer 'name'). Anyway, mit 'A. *' wird in der Regel als schlechte Praxis angesehen. – leonbloy

0

Versuchen Sie, diese

select A.id,B.title,A.description from TableA as A inner join tableB as B 
on A.id=B.id where B.table_A_id = 5 and B.langcode ='nl' 
+0

Diese Abfrage gibt A.name nicht zurück, wenn B.title nicht existiert. – EarthMind

1

Ok, ich bin nicht sicher, wie Sie Ihre Tabellen zu verbinden, aber so etwas wie dies sollte die Arbeit machen:

SELECT   yourcolumnlist, 
        CASE WHEN A.name IS NULL THEN B.title ELSE A.name END 
FROM    TableA AS A 
INNER JOIN  TableB AS B 
ON    A.id = B.table_A_id 
WHERE    B.table_A_id = 5 
AND    B.langcode = 'nl' 

Eine andere Möglichkeit wäre es zu tun sein, den COALESCE zu verwenden() Funktion:

SELECT   yourcolumnlist, 
        COALESCE(A.name, B.title) 
FROM    TableA AS A 
INNER JOIN  TableB AS B 
ON    A.id = B.table_A_id 
WHERE    B.table_A_id = 5 
AND    B.langcode = 'nl' 
+0

Beide Abfragen geben keine Zeilen zurück. Ich muss darauf hinweisen, dass, wenn Tabelle B keine Zeilen hat, in denen langcode = 'nl' ist, sie trotzdem alle Zeilen aus Tabelle A zurückgeben sollte.Und wenn es Zeilen gibt, in denen langcode = 'nl' ist, sollte das Feld wie in meinem ersten Post erwähnt ersetzt werden. – EarthMind

2
select A.id, coalesce(B.title, A.name) 
from TableA AS A 
    left join (select table_a_id, title from TableB where langcode = 'nl') AS B 
     on B.table_a_id = A.id 
WHERE A.id = 5 
+0

Das ist, was Sie wollen, denke ich. Der einzige Nachteil besteht darin, dass dies möglicherweise mehr als eine Zeile pro TabelleA-Zeile ergeben kann, wenn mehr als eine übereinstimmende TabelleB-Zeile vorhanden ist. – leonbloy

+0

Dies verhält sich auch nicht wie gewünscht. Es gibt jedesmal A.name zurück, unabhängig davon, ob B.title existiert oder nicht. – EarthMind

+0

+1 Das sollte funktionieren und ist im Allgemeinen besser als meine Antwort. – leonbloy