Quel est le résultat correct pour cette requête?


20

Je suis tombé sur ce puzzle dans les commentaires ici

CREATE TABLE r (b INT);

SELECT 1 FROM r HAVING 1=1;

SQL Server et PostgreSQL renvoient 1 ligne.

MySQL et Oracle ne renvoient aucune ligne.

Qui est correct? Ou les deux sont-ils également valables?


Joli puzzle. Je pense que le bon est de retourner 1 ligne. SQL-Server se contredit cependant car SELECT COUNT(*) FROM r;renvoie 1 ligne (avec 0), tandis qu'il SELECT COUNT(*) FROM r GROUP BY ();ne renvoie aucune ligne.
ypercubeᵀᴹ

1
Vouloir plus? SELECT 1 WHERE 1=0 HAVING 1=1;. SQL Server et PostgreSQL renvoient toujours une ligne. Oracle veut FROM DUAL et ne renvoie aucune ligne. MySQL ne compile ni avec FROM DUAL ni sans .
Andriy M

1
@AndriyM Pour une raison inconnue, "dual" et "HAVING" ne jouent pas bien dans MySQL. (Belle conclusion). Mais l'équivalent fonctionne: SELECT 1 AS t FROM (SELECT 1) tmp WHERE 1=0 HAVING 1=1; 1 ligne sans double et renvoie 0 lignes.
ypercubeᵀᴹ

1
@SQLKiwi - Qu'en est-il de ce passage de la spécification. "Si TE ne contient pas immédiatement un <group by clause>, alors il “GROUP BY ()”est implicite.". Les deux requêtes ne devraient-elles pas alors renvoyer les mêmes résultats?
Martin Smith

1
Mais désaccord sur ceux-ci (Oracle exécute les requêtes HAVINGdifféremment): SQl-fiddle 2: HAVING rend les choses différentes
ypercubeᵀᴹ

Réponses:


17

Selon la norme:

SELECT 1 FROM r HAVING 1=1

veux dire

SELECT 1 FROM r GROUP BY () HAVING 1=1

Citation ISO / IEC 9075-2: 2011 7.10 Règle de syntaxe 1 (partie de la définition de la clause HAVING):

Soit HCle <having clause>. Laissez - TEle <table expression>contenant immédiatement HC. Si TEne contient pas immédiatement un <group by clause>, « GROUP BY ()» est implicite. Soit Tle descripteur de la table défini par <group by clause> GBCimmédiatement contenu dans TEet Rsoit le résultat de GBC.

Ok donc c'est assez clair.


Assertion: 1=1est une véritable condition de recherche. Je ne fournirai aucune citation à ce sujet.


Maintenant

SELECT 1 FROM r GROUP BY () HAVING 1=1

équivaut à

SELECT 1 FROM r GROUP BY ()

Citation ISO / IEC 9075-2: 2011 7.10 Règle générale 1:

Le <search condition>est évalué pour chaque groupe de R. Le résultat de <having clause>est un tableau groupé des groupes de R pour lesquels le résultat de <search condition>est True.

Logique: Puisque la condition de recherche est toujours vraie, le résultat est R, qui est le résultat du groupe par expression.


Ce qui suit est un extrait des Règles générales de 7.9 (la définition du GROUPE PAR CLAUSE)

1) Si non <where clause>est spécifié, alors Tsoit le résultat de ce qui précède <from clause>; sinon, Tsoit le résultat de ce qui précède <where clause>.

2) Cas:

a) S'il n'y a pas de colonnes de regroupement, le résultat du <group by clause>est la table groupée constituée Tcomme son seul groupe.

Nous pouvons donc conclure que

FROM r GROUP BY ()

donne une table groupée, composée d'un groupe, avec zéro ligne (puisque R est vide).


Un extrait des règles générales de 7.12, qui définit une spécification de requête (alias une instruction SELECT):

1) Cas:

a) Si Tn'est pas une table groupée, alors [...]

b) Si Test une table groupée, alors

Cas:

i) Si Ta 0 (zéro) groupes, alors laissez TEMP être une table vide.

ii) Si Ta un ou plusieurs groupes, alors chacun <value expression>est appliqué à chaque groupe de Tproduire une table TEMPde Mlignes, où Mest le nombre de groupes dans T. La icolonne -th de TEMP contient les valeurs dérivées de l'évaluation du i-th <value expression>. [...]

2) Cas:

a) Si le <set quantifier> DISTINCTn'est pas spécifié, le résultat de l' <query specification>est TEMP.

Par conséquent, puisque la table a un groupe, elle doit avoir une ligne de résultat.

Donc

SELECT 1 FROM r HAVING 1=1

doit renvoyer un jeu de résultats d'une ligne.

QED


+1 Merci pour tous ces ennuis! Comme @ypercube le dit, SQL Server semble se contredire ici comme SELECT 1 FROM r GROUP BY (); renvoie zéro ligne mais le passage que vous avez cité semble assez clair sur ce point.
Martin Smith

Puis-je demander où avez-vous trouvé la norme? Si vous dites «sur ma bibliothèque», je serai déçu :)
dezso

Techniquement, j'ai utilisé le projet final de norme internationale plutôt que la norme elle-même. Conformément aux règles ISO / CEI, seules les modifications rédactionnelles (non techniques) sont autorisées entre FDIS et la norme finale. La norme est crachée en plusieurs parties. Partie 1 , Partie 2 , Partie 4 ...
Kevin Cathcart

Partie 11 et partie 14 . Les parties 3, 9, 10 et 13 n'ont pas été mises à jour en 2011, et donc leurs versions précédentes s'appliquent. Il n'y a pas de partie 12. De même, il n'y a pas de parties 5-8. Voir la page Wikipedia pour Sql: 2011 ou la partie 1 elle-même pour une explication de ce que chaque partie contient.
Kevin Cathcart

7

Lorsqu'il y a une HAVINGclause, sans WHEREclause:

SELECT 1 FROM r HAVING 1=1;

... GROUP BY ()est alors implicite. Ainsi, la requête doit être équivalente à:

SELECT 1 FROM r GROUP BY () HAVING 1=1;

... qui devrait regrouper toutes les lignes du tableau en un seul groupe (même si le tableau n'a pas de lignes du tout - c'est toujours un groupe de 0 lignes) et retourner 1 ligne. La HAVINGla Truecondition devrait avoir aucun effet après.


Sous un angle différent, combien de lignes une requête comme celle-ci doit-elle renvoyer?

SELECT COUNT(*), MAX(b) FROM r;

Un, zéro ou "zéro ou un, selon que la table est vide ou non"?

Je pense qu'une ligne, peu importe le nombre de lignes r.


Eh bien, le problème clé est de savoir s'il est vrai que "même si la table n'a pas de lignes du tout, c'est toujours un groupe de 0 lignes". Et la norme se révèle explicite à ce sujet: "S'il n'y a pas de colonnes de regroupement, alors ... est la table groupée constituée de T comme seul groupe". (et cela vaut même si T est vide - il y a donc bien un groupe.) Plus loin, la clause having spécifie que la condition est appliquée à chaque groupe (dans l'exemple donc une fois). Ils l'ont probablement défini de cette façon pour que SUM et COUNT retournent une ligne même pour des T vides.
Erwin Smout

+1 (plus tôt!) Même si votre logique est la même que celle de Kevin, j'ai accepté sa réponse en raison des citations de la spécification. Merci!
Martin Smith

@MartinSmith. Thnx. Que je reçois d'être paresseux :)
ypercubeᵀᴹ

@ypercube: +1 de moi aussi. J'ai décidé de prendre le temps supplémentaire de tirer de la spécification pour prouver qu'il n'y avait aucun mot de belette caché quelque part qui rendrait votre réponse fausse. Mais une fois que je l'ai fait, je pourrais aussi bien l'afficher comme réponse complète. Alors je l'ai fait.
Kevin Cathcart

3
@ErwinSmout: Bien sûr que non. Cependant, cela relève de l'utilisation équitable en vertu de la loi américaine sur le droit d'auteur. Portions relativement petites, citées dans le contexte de l'analyse (c.-à-d. Critique) de l'œuvre, à des fins pédagogiques, avec un impact négligeable sur la capacité de l'œuvre à être vendue.
Kevin Cathcart

3

D'après ce que je vois, il semble que SQLServer et PostgerSQL ne se soucient pas du tout de la table:

CREATE TABLE r (b INT);
insert into r(b) values (1);
insert into r(b) values (2);
SELECT 1 FROM r HAVING 1=1;

renvoie également une seule ligne. Même si les documents SQLServer disent

Lorsque GROUP BY n'est pas utilisé, HAVING se comporte comme une clause WHERE.

ce n'est pas vrai dans ce cas - WHERE 1=1au lieu de HAVINGrenvoie le nombre approprié de lignes. Je dirais que c'est un bogue de l'optimiseur (ou au moins un bogue de documentation) ... Le plan SQLServer affiche "Analyse constante" en cas de HAVINGet "analyse de table" pour WHERE...

Le comportement d'Oracle et de Mysql me semble plus logique et correct ...


1
Vous avez raison de dire que SQL Server ne regarde pas la table. Le plan d'exécution a juste un scan constant et ne fait même pas référence à la table. Si ce n'était que SQL Server, je l'aurais simplement mis à un bogue, mais comme ce n'est pas seulement SQL Server, je me demande s'il y a une véritable ambiguïté ici.
Martin Smith

PostgreSQL affiche les mêmes résultats que SQLServer, et pour autant que je sache à partir de la sortie de explain"Résultat (lignes = 1) ..." pour avoir et "Seq Scan" pour "OERE", il ne regarde pas non plus dans le tableau. .. Je suppose que c'est en quelque sorte lié au fait que "FROM" n'est pas obligatoire dans TSQL et PostgreSQL. Je sais que Mysql ne l'exige pas non plus, mais comme ils prennent en charge dual, ils analysent probablement la requête un peu différemment. Je suis d'accord, cela ressemble à une spéculation, mais j'espère que cela a du sens.
a1ex07
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.