¿Cómo verificar si una subconsulta tiene exactamente un resultado distinto y un valor especificado de forma concisa?

10

Me encontré escribiendo lo siguiente:

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

y preguntándome si hay una forma más concisa sin sacrificar demasiada legibilidad.

Encontré una forma en la que estoy publicando como respuesta, pero no estoy del todo contento y me interesarían mucho las alternativas.

En este caso vales único en el interior foo: no hay duplicados

Jack dice que intente topanswers.xyz
fuente
¿Entiendo correctamente que desea exactamente una fila en el resultado de la subconsulta?
Erwin Brandstetter
¿Qué subconsulta?
Jack dice que intente topanswers.xyz
El que mencionas en el título. No estaba seguro de si debería ser un resultado después o antes de "distinto".
Erwin Brandstetter
Ah sí, esa :) Me refería bastante confusamente a la subconsulta en mi respuesta : la suya es mucho más específica y flexible, por ejemplo, también puede usarla count(distinct val), aunque en mi caso del mundo real no hay diferencia
Jack dice que intente topanswers.xyz

Respuestas:

8

Conciso, rápido (especialmente con muchas filas), mi favorito con respecto a la legibilidad y también funcionaría con engaños:

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

Devuelve TRUE/ FALSE.. o NULL- solo en el caso de exactamente una fila con val IS NULL, porque count()nunca devuelve NULLo no hay fila.

El segundo 1en el ejemplo resulta ser el mismo que el primero, debido a su ejemplo.


La consulta en la pregunta falla con los NULLvalores. Considere la demostración 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 FROMsolucionaría esto, pero aún podría fallar con duplicados val, lo que ha descartado para este caso.


Tu respuesta funciona bien.
Devoluciones 'yes'/ sin fila.

Sin embargo, preferiría esta forma más corta. No olvide que PostgreSQL (a diferencia de Oracle) tiene un booleantipo adecuado .

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

Devoluciones TRUE/ FALSE/ NULL.

Erwin Brandstetter
fuente
excelente, gracias, sabía que habría una mejor manera :)
Jack dice que intente topanswers.xyz
5

Una variación de la respuesta de @ Erwin. No COUNT()en absoluto, solo MIN()y MAX(). Puede ser un poco más eficiente con una tabla grande y (no en su caso) duplicar val:

SELECT MIN(val) = 1 AND MAX(val) = 1 FROM foo;
ypercubeᵀᴹ
fuente
+1 gracias. Maneja nulos y duplicados de manera diferente, por supuesto (si hubiera alguno)
Jack dice que intente topanswers.xyz
@ Jack: Sí. ¿Tu mesa tiene nulos? ¿O quieres respuestas para ambos casos (con y sin)?
ypercubeᵀᴹ
no la mía no - Puedo usar cualquiera :)
Jack dice que intente topanswers.xyz
Sería mucho más rápido en tablas más grandes con un índice coincidente, pero funciona de manera idéntica en ausencia de dicho índice, como cuando se prueban los resultados de la consulta.
Erwin Brandstetter
3
select 'yes' where (select array_agg(val) from foo)=array[1];
Jack dice que intente topanswers.xyz
fuente
1

Este regresa true, falseo un resultado vacío:

 select j.val is null 
 from foo left join foo as j on j.val <> foo.val 
 where foo.val = 1 limit 1;
cáñamo gris
fuente
a primera vista, esto no parece regresar falsesi hay valores en foodónde val<>1?
Jack dice que intente topanswers.xyz
@JackDouglas Oh, lo siento. Entendí la tarea mal la primera vez. Fijo.
grayhemp
Funciona, excepto con un NULLvalor que no se ha descartado en este caso.
Erwin Brandstetter
@ErwinBrandstetter NULLse puede resolver usando IS [NOT] DISTINCT FROMCreo.
grayhemp
1
@grayhemp: No en este caso. LEFT JOIN foo j ON j.val <> foo.valno puede detectar una fila con j.val IS NULLpara empezar. Si lo incluye con ON j.val IS DISTINCT FROM foo.val, entonces debería verificar en otra columna de jdefinido NOT NULLpara distinguir los dos casos. Pero no se define una columna adicional.
Erwin Brandstetter