Jak sprawdzić, czy podkwerenda ma dokładnie jeden wyraźny wynik i określoną wartość w zwięzły sposób?

10

Odkryłem, że piszę:

select 'yes' 
where exists(select * from foo where val=1)
and not exists(select * from foo where val<>1);

i zastanawiam się, czy istnieje bardziej zwięzły sposób bez poświęcania zbyt dużej czytelności.

Znalazłem jeden sposób, który publikuję jako odpowiedź, ale nie jestem do końca z niego zadowolony i byłbym bardzo zainteresowany alternatywami

W tym przypadku valjest unikalny w obrębie foo- nie ma duplikatów

Jack mówi, że spróbuj topanswers.xyz
źródło
Czy rozumiem poprawnie, że chcesz dokładnie jednego wiersza w wyniku podzapytania?
Erwin Brandstetter,
Które podzapytanie?
Jack mówi, że spróbuj topanswers.xyz
Ten, o którym wspomniałeś w tytule. Nie byłem pewien, czy powinien to być jeden wynik po, czy przed „wyraźnym”.
Erwin Brandstetter,
Ach tak, tamta :) W mojej odpowiedzi dość myląco odnosiłam się do zapytania podrzędnego - twoje jest o wiele bardziej szczegółowe i elastyczne, np. Możesz także użyć count(distinct val), choć w moim przypadku w rzeczywistości nie ma to różnicy
mówi Jack, spróbuj topanswers.xyz

Odpowiedzi:

8

Zwięzły, szybki (szczególnie z wieloma wierszami), mój ulubiony pod względem czytelności i działałby również z duplikatami:

SELECT count(*) = 1 AND min(val) = 1 FROM foo;

Zwraca TRUE/ FALSE.. lub NULL- tylko w przypadku dokładnie jednego wiersza z val IS NULL, ponieważ count()nigdy nie zwraca NULLlub nie ma wiersza.

Drugi 1w przykładzie po prostu jest taki sam jak pierwszy, z powodu twojego przykładu.


Zapytanie w pytaniu kończy się niepowodzeniem z NULLwartościami. Rozważ proste demo:

CREATE TABLE foo (id int, val int);
INSERT INTO foo VALUES (1, 1),(2, NULL);

SELECT 'yes' 
WHERE      EXISTS(SELECT * FROM foo WHERE val =  1)
AND    NOT EXISTS(SELECT * FROM foo WHERE val <> 1);

IS DISTINCT FROMnaprawiłoby to, ale nadal może się nie powieść z duplikatami w val- co wykluczyłeś w tym przypadku.


Twoja odpowiedź działa dobrze.
Zwraca 'yes'/ brak wiersza.

Wolałbym jednak tę krótszą formę. Nie zapominaj, że PostgreSQL (w przeciwieństwie do Oracle) ma odpowiedni booleantyp .

SELECT array_agg(val) = array[1] FROM foo;

Zwraca TRUE/ FALSE/ NULL.

Erwin Brandstetter
źródło
świetnie, dziękuję, wiedziałem, że będzie lepszy sposób :)
Jack mówi, spróbuj wypróbować topanswers.xyz
5

Odmiana odpowiedzi @ Erwina. Nie COUNT()w ogóle, tylko MIN()i MAX(). Może być nieco bardziej wydajny z dużym stołem i (nie w twoim przypadku) duplikatem val:

SELECT MIN(val) = 1 AND MAX(val) = 1 FROM foo;
ypercubeᵀᴹ
źródło
+1 dzięki. Oczywiście traktuje wartości zerowe i duplikaty inaczej (jeśli były)
Jack mówi, spróbuj wypróbować topanswers.xyz
@ Jack: Tak. Czy twój stół ma wartości zerowe? A może chcesz odpowiedzi na oba przypadki (z i bez)?
ypercubeᵀᴹ
żadna moja nie ma - mogę też użyć :)
Jack mówi, spróbuj wypróbować topanswers.xyz
Byłoby znacznie szybciej na większych tabelach z pasującym indeksem, ale działa identycznie przy braku takiego indeksu - na przykład podczas testowania wyników zapytania.
Erwin Brandstetter,
3
select 'yes' where (select array_agg(val) from foo)=array[1];
Jack mówi, że spróbuj topanswers.xyz
źródło
1

Ten jeden powraca true, falselub pusty wynik:

 select j.val is null 
 from foo left join foo as j on j.val <> foo.val 
 where foo.val = 1 limit 1;
grayhemp
źródło
na pierwszy rzut oka wydaje się, że to nie zwraca, falsejeśli istnieją wartości foogdzie val<>1?
Jack mówi, że spróbuj topanswers.xyz
@JackDouglas Och, przepraszam. Za pierwszym razem źle zrozumiałem to zadanie. Naprawiony.
grayhemp,
Działa - z wyjątkiem NULLwartości, która nie została wykluczona w tym przypadku.
Erwin Brandstetter,
@ErwinBrandstetter NULLmożna opracować za pomocą, IS [NOT] DISTINCT FROMjak sądzę.
grayhemp
1
@grayhemp: Nie w tym przypadku. LEFT JOIN foo j ON j.val <> foo.valna początku nie wykrywa wiersza j.val IS NULLz. Jeśli dołączysz to ON j.val IS DISTINCT FROM foo.val, musisz sprawdzić kolejną kolumnę jzdefiniowaną, NOT NULLaby odróżnić te dwa przypadki. Ale nie zdefiniowano żadnej dodatkowej kolumny.
Erwin Brandstetter,