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


Czy rozumiem poprawnie, że chcesz dokładnie jednego wiersza w wyniku podzapytania?
Erwin Brandstetter,


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.


ś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;

+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,


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;

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,
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.