Uzyskiwanie instrukcji SELECT, aby zwracała stałą wartość, nawet jeśli pasują zero wierszy


15

Rozważ tę wybraną instrukcję:

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

Zwraca kolumnę query_idz wartością 1wraz z innymi kolumnami gracza.

Jak sprawić, by powyższy SQL zwrócił przynajmniej ten query_idz, 1nawet jeśli zaznaczenie nie znajdzie pasujących wierszy?

BTW, to PostgreSQL 8.4.

Odpowiedzi:


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');

Lub jako alternatywa ( może być szybsza, ponieważ nie jest wymagany drugi podselekcja):

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

Możesz ponownie napisać powyższe w bardziej „zwartej” reprezentacji:

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

Ale myślę, że wyraźne CTE ( with...) jest bardziej czytelne (chociaż zawsze jest to w oczach patrzącego).


1
Przy wypróbowywaniu pierwszego przykładu wydaje się, że słowo kluczowe ALL nie jest potrzebne?
Nathanael Weiss

2
@NatWeiss: jeśli potrzebujesz konkretnego zlecenia, to mają do dostarczenia order by. Drugi „tworzy” wirtualną tabelę z dokładnie jednym rzędem i jedną kolumną i wykonuje zewnętrzne połączenie (bez żadnych „rzeczywistych” warunków łączenia), dlatego zawsze otrzymujesz co najmniej ten jeden wiersz. Używanie select *w kodzie produkcyjnym jest złym stylem. Nie rób tego Zawsze wymieniaj kolumny, których potrzebujesz. select *powinien być używany tylko w zapytaniach ad-hoc.
a_horse_w_no_name

2
@NatWeiss: do jakiej „alternatywnej składni” dla „innych sprzężeń” masz na myśli. A jak myślisz, dlaczego nie left joinjest czytelny?
a_horse_w_no_name

2
@NatWeiss: niejawne sprzężenie w klauzuli where jest złym stylem kodowania i należy tego unikać. Może to prowadzić do niechcianych przyłączeń kartezjan bez błędu. I wyraźnie oddziela dwie (relacyjne) koncepcje łączenia i filtrowania
a_horse_w_no_name

4
re: modyfikator „all” klauzuli „union” nie jest potrzebny: UNION ALLmoże czasami być bardziej wydajny niż UNION, ponieważ wyraźnie powiadamiasz planistę zapytań, że albo spodziewasz się, że nie będzie żadnych zduplikowanych wierszy z UNIONzapytań ed lub jeśli tam chcesz, żeby były wyprowadzane. Bez ALLmodyfikatora zakłada się, że chcesz usunąć zduplikowane wiersze (tylko jeden z każdego zwróconego), podobnie jak w przypadku DISTINCTsłowa kluczowego, i aby zagwarantować, że może być konieczne dodatkowe użycie + ponownego przeskanowania wyników. Więc używaj ALLz, UNIONchyba że potrzebujesz konkretnej duplikacji wiersza wyjściowego.
David Spillett

7

Jeśli oczekujesz tylko jednego lub zerowego wiersza z powrotem, to również działałoby:

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

Zwróci jeden wiersz ze wszystkimi wartościami o wartości null, z wyjątkiem query_id, jeśli nie zostanie znaleziony wiersz.


2
Niezła sztuczka. Jedyną wadą jest to, że wartości dla col1 i col2 mogą nie należeć do tego samego wiersza, jeśli istnieje więcej niż jeden spełniający warunekusername = 'foobar'
a_horse_w_nazwie

1
Czy coalesce () może być również używane w ten sposób?
Nathanael Weiss

1
Koalescencja nie wygenerowałaby wiersza, w którym żaden nie jest rzutowany ze stołu.
David Aldridge

1
@ koń_nazwa_nazwa tak, chociaż nazwy tabeli i kolumny sugerują, że predykat znajduje się na kluczu kandydującym do tabeli, więc zostanie wyświetlony zero lub jeden wiersz.
David Aldridge

3

Chiming tutaj późno tutaj, ale oto składnia, która działa (przynajmniej w wersji 9.2, nie wypróbowałem wcześniejszych wersji).

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;

Zwróci „pusty” wiersz tylko wtedy, gdy cała zawartość „a” jest pusta.

Cieszyć się. / bithead


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.