La spécification SQL requiert-elle un GROUP BY dans EXISTS ()

11

Microsoft autorise actuellement cette syntaxe.

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
);

Notez qu'il n'y a pas GROUP BYdans la EXISTSclause, c'est que SQL ANSI valide. Ou s'agit-il simplement d'exposer un détail d'implémentation.

Pour référence, cette même syntaxe n'est pas autorisée dans PostgreSQL.

ERREUR: la colonne "tx" doit apparaître dans la clause GROUP BY ou être utilisée dans une fonction d'agrégation

Mais cette syntaxe est autorisée ..

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
);

Et cette syntaxe est autorisée.

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
);

La question découle d' une conversation avec @ErikE dans le chat

Evan Carroll
la source

Réponses:

11

Je l'ai trouvé dans la spécification SQL 2011 ...

Si le <select list>«*» est simplement contenu dans un <table subquery>qui est immédiatement contenu dans un <exists predicate>, alors l' <select list>équivalent d'un <value expression>qui est arbitraire <literal>.

Cela confirme qu'en *n'étant pas équivalent à un littéral arbitraire dans ce contexte, c'est en fait PostgreSQL qui rompt la spécification.

Gardez à l'esprit que c'est un problème distinct de

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

Ce que les deux bases de données rejettent.

PostgreSQL,

ERREUR: la colonne "tx" doit apparaître dans la clause GROUP BY ou être utilisée dans une fonction d'agrégation

Serveur SQL,

La colonne 'tx' n'est pas valide dans la liste de sélection car elle n'est contenue ni dans une fonction d'agrégation ni dans la clause GROUP BY.

Pourquoi ce bogue persiste dans PostgreSQL

Merci à RhodiumToad sur irc.freenode.net/#PostgreSQL pour son aide à résoudre ce problème. Il souligne également la difficulté de résoudre cette situation

20:33 <RhodiumToad> le seul problème est que dans pg vous pouvez faire existe (sélectionnez func () à partir de ... où func () est un SRF qui pourrait retourner 0 lignes

Un SRF est une fonction de retour d'ensemble.

Dans PostgreSQL, nous pouvons par exemple utiliser un SRF pour générer une série de 1 à 10 ( generate_seriesest dans le noyau)

SELECT * FROM generate_series(1,10); 

Et, nous pouvons également le mettre ici.

SELECT generate_series(1,10);

Deux d'entre eux ensemble nous donnent une jointure croisée (produit cartésien)

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

Mais, si l'un de ces renvois 0 lignes, vous n'obtenez rien .. En fait, le même que celui-ci

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

Et, c'est le problème avec l'optimisation complète. Vous pouvez avoir un SRF dans une liste de sélection à l'intérieur d'une instruction EXIST qui renvoie 0 lignes et force les EXISTS à évaluer à faux.

Evan Carroll
la source