O SQL Spec requer um GROUP BY em EXISTS ()

11

Atualmente, a Microsoft permite essa sintaxe.

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT *
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  HAVING count(*) > 1
);

Observe que não há GROUP BYna EXISTScláusula, é esse ANSI SQL válido. Ou é apenas expor um detalhe de implementação.

Para referência, essa mesma sintaxe não é permitida no PostgreSQL.

ERRO: a coluna "tx" deve aparecer na cláusula GROUP BY ou ser usada em uma função agregada

Mas essa sintaxe é permitida ..

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT 1  -- This changed from the first query
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  HAVING count(*) > 1
);

E essa sintaxe é permitida.

SELECT *
FROM ( VALUES (1) ) AS g(x)
WHERE EXISTS (
  SELECT *
  FROM ( VALUES (1),(1) )
    AS t(x)
  WHERE g.x = t.x
  GROUP BY t.x  -- This changed from the first query
  HAVING count(*) > 1
);

Pergunta surge de uma conversa com @ErikE no chat

Evan Carroll
fonte

Respostas:

11

Eu o encontrei na especificação do SQL 2011 ...

Se o <select list>"*" é simplesmente contido em um <table subquery>que é contido imediatamente em um <exists predicate>, então <select list>é equivalente a um <value expression>que é arbitrário <literal>.

Isso confirma que, por *não ser equivalente a um literal arbitrário nesse contexto , é na verdade o PostgreSQL quebrando a especificação.

Lembre-se de que este é um problema distinto de

SELECT *
FROM ( VALUES (1),(2),(3) ) AS t(x)
HAVING count(*) > 1

Que ambos os bancos de dados rejeitam.

PostgreSQL,

ERRO: a coluna "tx" deve aparecer na cláusula GROUP BY ou ser usada em uma função agregada

Servidor SQL,

A coluna 'tx' é inválida na lista de seleção porque não está contida em uma função agregada ou na cláusula GROUP BY.

Por que esse bug persiste no PostgreSQL

Agradecemos a RhodiumToad em irc.freenode.net/#PostgreSQL por sua ajuda na resolução de problemas. Ele também aponta a dificuldade em resolver essa situação

20:33 <RhodiumToad> o único problema é que, em pg, você pode existir (selecione func () em ... onde func () é um SRF que pode retornar 0 linhas

Um SRF é uma função de retorno definida.

No PostgreSQL, podemos usar, por exemplo, um SRF para gerar uma série de 1 a 10 ( generate_seriesestá no núcleo)

SELECT * FROM generate_series(1,10); 

E, da mesma forma, podemos colocá-lo aqui.

SELECT generate_series(1,10);

Dois deles juntos nos dão uma junção cruzada (produto cartesiano)

SELECT generate_series(1,10), generate_series(1,2);

Mas, se qualquer um desses retornar 0 linhas, você não obterá nada. Efetivamente, o mesmo que este

SELECT * FROM ( VALUES (1) ) AS t(x)
CROSS JOIN ( SELECT 1 LIMIT 0 ) AS g;

E esse é o problema de otimizar isso totalmente. Você pode ter um SRF em uma lista de seleção dentro de uma instrução EXIST que retorna 0 linhas e força os EXISTS a avaliarem como false.

Evan Carroll
fonte