Mit SELECT wird ein konstanter Wert zurückgegeben, auch wenn keine Zeilen übereinstimmen


15

Betrachten Sie diese select-Anweisung:

SELECT *, 
       1 AS query_id 
FROM players 
WHERE username='foobar';

Es gibt die Spalte query_idmit dem Wert 1zusammen mit den anderen Spalten eines Spielers zurück.

Wie würde man die obige SQL-Anweisung dazu bringen, zumindest die query_idvon 1selbst zurückzugeben, wenn die Auswahl keine übereinstimmenden Zeilen findet?

Übrigens ist es PostgreSQL 8.4.

Antworten:


22
SELECT col1, 
       col2, 
       col3, 
       1 AS query_id 
FROM players 
WHERE username='foobar'
union all 
select null,
       null,
       null,
       1
where not exists (select 1 from players where username = 'foobar');

Oder als Alternative ( möglicherweise schneller, da keine zweite Unterauswahl erforderlich ist):

with qid (query_id) as (
   values (1)
) 
select p.*, 
       qid.query_id
from qid 
  left join players as p on (p.useranme = 'foobar');

Sie können das Obige in eine "kompaktere" Darstellung umschreiben:

select p.*, 
       qid.query_id
from (values (1)) as qid (query_id)
  left join players as p on (p.useranme = 'foobar');

Aber ich denke das explizite CTE ( with...) ist besser lesbar (obwohl das immer in den Augen des Betrachters liegt).


1
Beim Ausprobieren des ersten Beispiels scheint das Schlüsselwort ALL nicht erforderlich zu sein.
Nathanael Weiss

2
@NatWeiss: wenn Sie einen bestimmten Auftrag benötigen, Sie haben eine liefern order by. Die zweite "erstellt" eine virtuelle Tabelle mit genau einer Zeile und einer Spalte und führt einen Outer-Join durch (ohne "echte" Join-Bedingung), sodass Sie immer mindestens diese eine Zeile zurückerhalten. Die Verwendung von select *Code in der Produktion ist ein schlechter Stil. Tu es nicht. Listen Sie immer die Spalten auf, die Sie benötigen. select *sollte nur in Ad-hoc-Abfragen verwendet werden.
a_horse_with_no_name

2
@NatWeiss: Auf welche "alternative Syntax" für "andere Verknüpfungen" beziehen Sie sich? Und warum denkst du, left joinist nicht lesbar?
a_horse_with_no_name

2
@ NatWeiss: Der implizite Join in der where-Klausel ist ein schlechter Codierungsstil und sollte vermieden werden. Es kann zu unerwünschten Kartesier-Verknüpfungen kommen, ohne dass Sie einen Fehler erhalten. Und es trennt klar die beiden (relationalen) Konzepte des Verbindens und
Filterns

4
Betreff: Der Modifikator "all" der "union" -Klausel wird nicht benötigt: UNION ALLKann manchmal effizienter sein als UNION, da Sie dem Abfrageplaner explizit mitteilen, dass entweder erwartet wird, dass keine doppelten Zeilen aus den UNIONed-Abfragen kommen, oder wenn Sie möchten, dass sie ausgegeben werden. Ohne den ALLModifikator wird davon ausgegangen, dass Sie doppelte Zeilen entfernen möchten (von denen jeweils nur eine zurückgegeben wird), ähnlich wie beim DISTINCTSchlüsselwort, und um sicherzustellen, dass die Ergebnisse möglicherweise erneut durchsucht werden müssen. Verwenden Sie also ALLmit, es UNIONsei denn, Sie benötigen speziell die Deduplizierung von Ausgabezeilen.
David Spillett

7

Wenn Sie nur eine oder null Zeilen zurück erwarten, dann würde dies auch funktionieren:

SELECT
  max(col1) col1,
  max(col2) col2, 
  1 AS query_id 
FROM
  players 
WHERE
  username='foobar';

Dies gibt eine Zeile mit allen Werten mit Null zurück, außer query_id, wenn keine Zeile gefunden wird.


2
Guter Trick. Der einzige Nachteil ist, dass die Werte für col1 und col2 möglicherweise nicht zur selben Zeile gehören, wenn mehr als einer der Bedingungen entsprichtusername = 'foobar'
a_horse_with_no_name 27.04.13

1
Könnte coalesce () auch auf diese Weise verwendet werden?
Nathanael Weiss

1
Coalesce generiert keine Zeile, in der keine aus der Tabelle projiziert wird.
David Aldridge

1
@a_horse_with_no_name yes, obwohl die Tabellen- und Spaltennamen darauf hindeuten, dass sich das Prädikat auf einem Kandidatenschlüssel für die Tabelle befindet, sodass keine oder eine Zeile projiziert wird.
David Aldridge

3

Ich stimme hier spät zu, aber hier ist eine Syntax, die funktioniert (zumindest in 9.2, ich habe frühere Versionen nicht ausprobiert).

SELECT (COALESCE(a.*,b.*::players)).*
FROM ( SELECT col1,  col2,  col3, 1 AS query_id 
       FROM players WHERE username='foobar' ) a
RIGHT JOIN (select null col1, null col2, null col3, 1 col4) b
ON a.query_id = b.col4;

Gibt die "leere" Zeile nur zurück, wenn der gesamte Inhalt von "a" null ist.

Genießen. / bithead


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.