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.