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


Entendi corretamente que você deseja exatamente uma linha no resultado da subconsulta?
Erwin Brandstetter


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.


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;

+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


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;

à 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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.