Come verificare se una sottoquery ha esattamente un risultato distinto e un valore specificato in modo conciso?


10

Mi sono trovato a scrivere quanto segue:

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

e chiedersi se esiste un modo più conciso senza sacrificare troppa leggibilità.

Ho trovato un modo in cui sto postando una risposta, ma non ne sono completamente soddisfatto e sarei molto interessato alle alternative

In questo caso valè unico all'interno foo- non ci sono duplicati


Capisco correttamente che si desidera esattamente una riga nel risultato della query secondaria?
Erwin Brandstetter,


Quello che citi nel titolo. Non ero sicuro che dovesse essere un risultato dopo o prima "distinto".
Erwin Brandstetter,

Ah sì, quello :) Mi riferivo piuttosto confusamente alla sottoquery nella mia risposta - la tua è molto più specifica e flessibile, ad esempio puoi anche usare count(distinct val), anche se nel mio caso reale non fa alcuna differenza
Jack dice di provare topanswers.xyz il

Risposte:


8

Conciso, veloce (specialmente con molte righe), il mio preferito in termini di leggibilità e funzionerebbe anche con i duplicati:

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

Restituisce TRUE/ FALSE.. o NULL- solo nel caso di esattamente una riga con val IS NULL, perché count()non restituisce mai NULLo nessuna riga.

Il secondo 1nell'esempio sembra essere uguale al primo, a causa del tuo esempio.


La query nella domanda ha esito negativo con i NULLvalori. Considera la semplice 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 FROMrisolverebbe questo problema, ma potrebbe comunque non riuscire con i duplicati in val- che hai escluso per questo caso.


La tua risposta funziona bene.
Restituisce 'yes'/ nessuna riga.

Preferirei questa forma più corta, però. Non dimenticare che PostgreSQL (a differenza di Oracle) ha un booleantipo corretto .

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

Restituisce TRUE/ FALSE/ NULL.


eccellente, grazie, sapevo che ci sarebbe stato un modo migliore :)
Jack dice di provare topanswers.xyz il

5

Una variazione sulla risposta di @Erwin. No COUNT()affatto, solo MIN()e MAX(). Potrebbe essere leggermente più efficiente con un grande tavolo e (non nel tuo caso) duplicato val:

SELECT MIN(val) = 1 AND MAX(val) = 1 FROM foo;

+1 grazie. Gestisce i null e i duplicati in modo diverso ovviamente (se ce ne fossero)
Jack dice di provare topanswers.xyz il

@ Jack: Sì. Il tuo tavolo ha valori null? O vuoi risposte per entrambi i casi (con e senza)?
ypercubeᵀᴹ

no il mio no - posso usare neanche :)
Jack dice di provare topanswers.xyz il

Sarebbe molto più veloce su tabelle più grandi con un indice corrispondente, ma si comporta in modo identico in assenza di tale indice, come quando si verificano i risultati delle query.
Erwin Brandstetter,


1

Questo si ritorna true, falseo un risultato vuoto:

 select j.val is null 
 from foo left join foo as j on j.val <> foo.val 
 where foo.val = 1 limit 1;

a prima vista, questo non sembra tornare falsese ci sono valori in foocui val<>1?
Jack dice di provare topanswers.xyz il

@JackDouglas Oh, scusa. Ho capito il compito sbagliato la prima volta. Fisso.
grayhemp,

Funziona - ad eccezione di NULLcome valore che non è stato escluso in questo caso.
Erwin Brandstetter,

@ErwinBrandstetter NULLpuò essere risolto usando IS [NOT] DISTINCT FROMcredo.
grayhemp,

1
@grayhemp: non in questo caso. LEFT JOIN foo j ON j.val <> foo.valnon riesce a rilevare una riga con j.val IS NULLper cominciare. Se lo includessi, ON j.val IS DISTINCT FROM foo.valdovresti controllare un'altra colonna di jdefinite NOT NULLper distinguere i due casi. Ma nessuna colonna aggiuntiva è definita.
Erwin Brandstetter,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.