Como verificar se uma subconsulta tem exatamente um resultado distinto e um valor especificado de forma concisa?

10

Eu me vi escrevendo o seguinte:

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

e me perguntando se existe uma maneira mais concisa sem sacrificar muita legibilidade.

Eu encontrei uma maneira que estou postando como resposta, mas não estou totalmente feliz com isso e estaria muito interessado em alternativas

Nesse caso, valé único dentro foo- não há duplicatas

Jack diz que tenta topanswers.xyz
fonte
Entendi corretamente que você deseja exatamente uma linha no resultado da subconsulta?
Erwin Brandstetter
Qual subconsulta?
Jack diz que tente topanswers.xyz
O que você mencionou no título. Eu não tinha certeza se deveria haver um resultado depois ou antes de "distinto".
Erwin Brandstetter
Ah sim, essa :) Eu estava me referindo confusa à subconsulta na minha resposta - a sua é muito mais específica e flexível, por exemplo, você também pode usar count(distinct val), embora no meu caso do mundo real não faça diferença
Jack diz que tenta topanswers.xyz 04/04/12

Respostas:

8

Conciso, rápido (especialmente com muitas linhas), o meu favorito em relação à legibilidade e também funcionaria com dupes:

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

Retorna TRUE/ FALSE.. ou NULL- apenas no caso de exatamente uma linha com val IS NULL, porque count()nunca retorna NULLou nenhuma linha.

O segundo 1no exemplo é o mesmo que o primeiro, por causa do seu exemplo.


A consulta na pergunta falha com NULLvalores. Considere a demonstração simples:

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 FROMcorrigiria isso, mas ainda assim poderia falhar com duplicatas val- as quais você descartou neste caso.


Sua resposta funciona bem.
Retorna 'yes'/ sem linha.

Eu preferiria essa forma mais curta, no entanto. Não esqueça que o PostgreSQL (ao contrário do Oracle) tem um booleantipo apropriado .

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

Retorna TRUE/ FALSE/ NULL.

Erwin Brandstetter
fonte
excelentes, obrigado, eu sabia que haveria uma maneira melhor :)
Jack diz tentativa topanswers.xyz
5

Uma variação na resposta de @ Erwin. Não COUNT(), apenas MIN()e MAX(). Pode ser um pouco mais eficiente com tabela grande e (não no seu caso) duplicada val:

SELECT MIN(val) = 1 AND MAX(val) = 1 FROM foo;
ypercubeᵀᴹ
fonte
+1 obrigado. Ele lida com valores nulos e duplica de forma diferente, é claro (se houver algum)
Jack diz tentativa topanswers.xyz
@ Jack: Sim. Sua tabela possui nulos? Ou você quer respostas para os dois casos (com e sem)?
usar o seguinte código
não meu não - posso usar :)
Jack diz tentar topanswers.xyz
Seria muito mais rápido em tabelas maiores com um índice correspondente, mas é executado de forma idêntica na ausência de um índice semelhante - ao testar os resultados da consulta.
Erwin Brandstetter
3
select 'yes' where (select array_agg(val) from foo)=array[1];
Jack diz que tenta topanswers.xyz
fonte
1

Este retorna true, falseou um resultado vazio:

 select j.val is null 
 from foo left join foo as j on j.val <> foo.val 
 where foo.val = 1 limit 1;
grayhemp
fonte
à primeira vista, isso não parece retornar falsese houver valores em fooonde val<>1?
Jack diz que tente topanswers.xyz
@JackDouglas Oh, desculpe. Eu entendi a tarefa errada da primeira vez. Fixo.
grayhemp
Funciona - exceto com um NULLvalor que não foi descartado neste caso.
Erwin Brandstetter
O @ErwinBrandstetter NULLpode ser resolvido usando IS [NOT] DISTINCT FROMeu acho.
grayhemp 8/09/12
11
@ grayhemp: Não neste caso. LEFT JOIN foo j ON j.val <> foo.valfalha ao detectar uma linha j.val IS NULLno início. Se você incluí- ON j.val IS DISTINCT FROM foo.vallo, precisará verificar outra coluna de jdefinido NOT NULLpara diferenciar os dois casos. Mas nenhuma coluna adicional está definida.
Erwin Brandstetter