Comment vérifier si une sous-requête a exactement un résultat distinct et une valeur spécifiée de manière concise?


10

Je me suis retrouvé à écrire ce qui suit:

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

et se demander s'il existe un moyen plus concis sans sacrifier trop de lisibilité.

J'ai trouvé un moyen que je poste comme réponse mais je ne suis pas entièrement satisfait et je serais très intéressé par les alternatives

Dans ce cas valest unique à l'intérieur foo- il n'y a pas de doublons


Dois-je bien comprendre que vous voulez exactement une ligne dans le résultat de la sous-requête?
Erwin Brandstetter

Quelle sous-requête?
Jack dit d'essayer topanswers.xyz le

Celui que vous mentionnez dans le titre. Je ne savais pas si ce devait être un résultat après ou avant "distinct".
Erwin Brandstetter du

Ah oui, celle-là :) Je faisais plutôt référence à la sous-requête dans ma réponse - la vôtre est beaucoup plus spécifique et flexible, par exemple, vous pouvez également l'utiliser count(distinct val), bien que dans mon cas réel cela ne fasse aucune différence
Jack dit essayer topanswers.xyz

Réponses:


8

Concis, rapide (surtout avec de nombreuses lignes), mon préféré concernant la lisibilité et fonctionnerait aussi avec les dupes:

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

Renvoie TRUE/ FALSE.. ou NULL- uniquement dans le cas d'exactement une ligne avec val IS NULL, car count()ne renvoie jamais NULLou aucune ligne.

Le second 1de l'exemple se trouve être le même que le premier, à cause de votre exemple.


La requête dans la question échoue avec des NULLvaleurs. Considérez la démonstration simple:

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 FROMpourrait résoudre ce problème, mais il pourrait toujours échouer avec des doublons dans val- ce que vous avez exclu dans ce cas.


Votre réponse fonctionne bien.
Renvoie 'yes'/ aucune ligne.

Je préférerais cependant cette forme plus courte. N'oubliez pas que PostgreSQL (contrairement à Oracle) a un booleantype approprié .

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

Renvoie TRUE/ FALSE/ NULL.


excellent, merci, je savais qu'il y aurait une meilleure façon :)
Jack dit essayer topanswers.xyz

5

Une variation sur la réponse de @ Erwin. Non COUNT()du tout, seulement MIN()et MAX(). Il peut être légèrement plus efficace avec une grande table et (pas dans votre cas) en double val:

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

+1 merci. Bien sûr, il gère les valeurs nulles et les doublons (s'il y en avait)
Jack dit essayer topanswers.xyz

@Jack: Oui. Votre table a-t-elle des valeurs nulles? Ou vous voulez des réponses pour les deux cas (avec et sans)?
ypercubeᵀᴹ

pas le mien non - je peux utiliser non plus :)
Jack dit d'essayer topanswers.xyz le

Serait beaucoup plus rapide sur des tables plus grandes avec un index correspondant, mais fonctionne de manière identique en l'absence d'un tel index, comme lors du test des résultats d'une requête.
Erwin Brandstetter


1

Celui-ci renvoie true, falseou un résultat vide:

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

à première vue, cela ne semble pas revenir falses'il y a des valeurs dans fooval<>1?
Jack dit d'essayer topanswers.xyz le

@JackDouglas Oh, désolé. J'ai mal compris la tâche la première fois. Fixé.
grayhemp

Fonctionne - sauf avec une NULLvaleur qui n'a pas été exclue dans ce cas.
Erwin Brandstetter

@ErwinBrandstetter NULLpeut être élaboré en utilisant IS [NOT] DISTINCT FROMJe pense.
grayhemp

1
@grayhemp: Pas dans ce cas. LEFT JOIN foo j ON j.val <> foo.valne parvient pas à détecter une ligne avec j.val IS NULLpour commencer. Si vous l'incluez avec ON j.val IS DISTINCT FROM foo.valvous, vous devrez alors vérifier sur une autre colonne de jdéfini NOT NULLpour distinguer les deux cas. Mais aucune colonne supplémentaire n'est définie.
Erwin Brandstetter du
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.