Was ist das richtige Ergebnis für diese Abfrage?

20

Ich bin auf dieses Rätsel in den Kommentaren hier gestoßen

CREATE TABLE r (b INT);

SELECT 1 FROM r HAVING 1=1;

SQL Server und PostgreSQL geben 1 Zeile zurück.

MySQL und Oracle geben keine Zeilen zurück.

Welches ist richtig? Oder sind beide gleich gültig?

Martin Smith
quelle
Schönes Puzzle. Ich denke, das Richtige ist, 1 Zeile zurückzugeben. SQL-Server widerspricht sich jedoch selbst, da SELECT COUNT(*) FROM r;1 Zeile (mit 0) zurückgegeben wird, während SELECT COUNT(*) FROM r GROUP BY ();keine Zeilen zurückgegeben werden.
ypercubeᵀᴹ
1
Mehr wollen? SELECT 1 WHERE 1=0 HAVING 1=1;. SQL Server und PostgreSQL geben weiterhin eine Zeile zurück. Oracle möchte FROM DUAL und gibt keine Zeilen zurück. MySQL kompiliert weder mit noch ohne FROM DUAL .
Andriy M
1
@AndriyM Aus irgendeinem unbekannten Grund spielen "dual" und "HAVING" in MySQL nicht gut. (Schöne Entdeckung). Aber das Äquivalent funktioniert: SELECT 1 AS t FROM (SELECT 1) tmp WHERE 1=0 HAVING 1=1; 1-row-no-dual und gibt 0 Zeilen zurück.
Ypercubeᵀᴹ
1
@ SQLKiwi - Was ist mit dieser Passage aus der Spezifikation. Msgstr "Wenn TE nicht sofort a enthält <group by clause>, “GROUP BY ()”ist implizit." Sollten dann nicht beide Abfragen die gleichen Ergebnisse liefern?
Martin Smith
1
Aber nicht einverstanden (Oracle führt Abfragen HAVINGanders aus): SQL-Fiddle 2: HAVING macht die Dinge anders
ypercubeᵀᴹ

Antworten:

17

Nach dem Standard:

SELECT 1 FROM r HAVING 1=1

meint

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

Zitat ISO / IEC 9075-2: 2011 7.10 Syntaxregel 1 (Teil der Definition der HAVING-Klausel):

Lass HCdas sein <having clause>. Sei TEdas <table expression>, was sofort enthält HC. Wenn TEnicht sofort ein enthalten ist <group by clause>, dann ist " GROUP BY ()" implizit. Sei Tder Deskriptor der Tabelle, die durch das <group by clause> GBCsofort enthaltene in definiert ist, TEund sei Rdas Ergebnis von GBC.

Ok, so viel ist ziemlich klar.


Behauptung: 1=1ist wahre Suchbedingung. Ich werde dafür kein Zitat geben.


Jetzt

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

ist gleichwertig mit

SELECT 1 FROM r GROUP BY ()

Zitat ISO / IEC 9075-2: 2011 7.10 Allgemeine Regel 1:

Das <search condition>wird für jede Gruppe von ausgewertet R. Das Ergebnis von <having clause>ist eine gruppierte Tabelle der Gruppen von R, für die das Ergebnis von <search condition>True ist.

Logik: Da die Suchbedingung immer wahr ist R, ist das Ergebnis das Ergebnis der Gruppierung nach Ausdrücken.


Das Folgende ist ein Auszug aus den Allgemeinen Regeln von 7.9 (die Definition der GROUP BY CLAUSE)

1) Wenn nein <where clause>angegeben ist, sei Tdas Ergebnis des vorhergehenden <from clause>; Ansonsten sei Tdas Ergebnis des Vorhergehenden <where clause>.

2) Fall:

a) Wenn es keine Gruppierungsspalten gibt, ist das Ergebnis von <group by clause>die gruppierte Tabelle, die Tals einzige Gruppe besteht.

Daraus können wir schließen

FROM r GROUP BY ()

ergibt eine gruppierte Tabelle, bestehend aus einer Gruppe, mit null Zeilen (da R leer ist).


Ein Auszug aus den Allgemeinen Regeln von 7.12, in dem eine Abfragespezifikation (auch als SELECT-Anweisung bezeichnet) definiert ist:

1) Fall:

a) Wenn Tes sich nicht um eine gruppierte Tabelle handelt, dann [...]

b) Wenn Tes sich um eine gruppierte Tabelle handelt, dann

Fall:

i) Wenn Tes 0 (Null) Gruppen gibt, dann sei TEMP eine leere Tabelle.

ii) Wenn Teine oder mehr Gruppen, die jeweils dann <value expression>auf jede Gruppe angewandt wird , Twodurch man eine Tabelle TEMPvon MZeilen, wobei Mdie Anzahl der Gruppen in T. Die i-te Spalte von TEMP enthält die Werte, die durch die Auswertung der i-ten abgeleitet wurden <value expression>. [...]

2) Fall:

a) Wenn das <set quantifier> DISTINCTnicht angegeben wird, dann ist das Ergebnis der <query specification>ist TEMP.

Da die Tabelle eine Gruppe enthält, muss sie daher eine Ergebniszeile enthalten.

Somit

SELECT 1 FROM r HAVING 1=1

sollte eine 1-zeilige Ergebnismenge zurückgeben.

QED

Kevin Cathcart
quelle
+1 Danke, dass du dir die Mühe gemacht hast! Wie @ypercube sagt, scheint sich SQL Server hier als SELECT 1 FROM r GROUP BY () zu widersprechen. gibt null Zeilen zurück, aber die von Ihnen angegebene Passage scheint in diesem Punkt ziemlich klar zu sein.
Martin Smith
Darf ich fragen, wo haben Sie den Standard gefunden? Wenn Sie "Auf meinem Bücherregal" sagen, werde ich enttäuscht sein :)
Dezso
Technisch habe ich eher den Final Draft International Standard als den Standard selbst verwendet. Gemäß den ISO / IEC-Regeln sind nur redaktionelle (nicht technische) Änderungen zwischen FDIS und der endgültigen Norm zulässig. Der Standard ist in mehrere Teile aufgeteilt. Teil 1 , Teil 2 , Teil 4 ...
Kevin Cathcart
Teil 11 und Teil 14 . Die Teile 3, 9, 10 und 13 wurden im Jahr 2011 nicht aktualisiert. Daher gelten die vorherigen Versionen. Es gibt keinen Teil 12. Ebenso gibt es keine Teile 5-8. Auf der Wikipedia-Seite für SQL: 2011 oder Teil 1 selbst finden Sie eine Erläuterung der einzelnen Teile.
Kevin Cathcart
7

Wenn es eine HAVINGKlausel ohne WHEREKlausel gibt:

SELECT 1 FROM r HAVING 1=1;

... dann GROUP BY ()ist implizit. Die Abfrage sollte also äquivalent sein zu:

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

... die alle Zeilen der Tabelle in einer Gruppe gruppieren sollte (auch wenn die Tabelle überhaupt keine Zeilen enthält - es ist immer noch eine Gruppe von 0 Zeilen) und 1 Zeile zurückgeben. Die HAVINGmit der TrueBedingung sollte danach überhaupt keine Wirkung haben.


Wie viele Zeilen sollte eine solche Abfrage aus einem anderen Blickwinkel zurückgeben?

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

Eins, Null oder "Null oder Eins, je nachdem, ob die Tabelle leer ist oder nicht"?

Ich denke, eine Zeile, egal wie viele Zeilen rhat.

ypercubeᵀᴹ
quelle
Nun, die entscheidende Frage ist, ob es in der Tat stimmt, dass "selbst wenn die Tabelle überhaupt keine Zeilen enthält, es sich immer noch um eine Gruppe von 0 Zeilen handelt". Und der Standard erweist sich diesbezüglich als explizit: "Wenn es keine Gruppierungsspalten gibt, dann ... ist die gruppierte Tabelle, die aus T als einziger Gruppe besteht". (Und das gilt auch, wenn T leer ist - es gibt also tatsächlich eine Gruppe.) Weiterhin gibt die having-Klausel an, dass die Bedingung auf jede Gruppe angewendet wird (im Beispiel also einmal). Sie haben es wahrscheinlich so definiert, dass SUM und COUNT auch für leere T eine Zeile zurückgeben.
Erwin Smout
+1 (früher!) Obwohl Ihre Logik mit der von Kevin identisch ist, habe ich seine Antwort aufgrund der Zitate aus der Spezifikation akzeptiert. Vielen Dank!
Martin Smith
@MartinSmith. Danke. Das bekomme ich, weil ich faul
bin
@ypercube: +1 von mir auch. Ich beschloss, mir etwas mehr Zeit zu nehmen, um zu beweisen, dass keine Wieselwörter versteckt waren, die Ihre Antwort falsch machen würden. Aber sobald ich das getan habe, könnte ich es genauso gut als vollständige Antwort posten. So tat ich.
Kevin Cathcart
3
@ ErwinSmout: Natürlich nicht. Dies fällt jedoch unter das US-amerikanische Urheberrecht. Relativ kleine Teile, die im Zusammenhang mit der Analyse (dh Kritik) der Arbeit zu Bildungszwecken angegeben werden und die die Verkaufsfähigkeit der Arbeit vernachlässigbar beeinträchtigen.
Kevin Cathcart
3

Wie ich sehe, scheinen SQLServer und PostgerSQL überhaupt nicht in die Tabelle zu schauen:

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

Gibt auch nur eine Zeile zurück. Auch wenn SQLServer docs sagt

Wenn GROUP BY nicht verwendet wird, verhält sich HAVING wie eine WHERE-Klausel.

Dies ist in diesem Fall nicht der Fall, WHERE 1=1sondern HAVINGgibt die richtige Anzahl von Zeilen zurück. Ich würde sagen, es ist ein Optimierungsfehler (oder zumindest ein Dokumentationsfehler) ... Der SQLServer-Plan zeigt "Constant Scan" für den Fall HAVINGund "Table Scan" für WHERE...

Das Verhalten von Oracle und MySQL erscheint mir logischer und korrekter ...

a1ex07
quelle
1
Sie haben Recht, dass SQL Server nicht auf die Tabelle schaut. Der Ausführungsplan hat nur einen konstanten Scan und verweist nicht einmal auf die Tabelle. Wenn es nur SQL Server wäre, hätte ich es einfach auf einen Fehler zurückgeführt, aber da es nicht nur SQL Server ist, frage ich mich, ob es hier echte Mehrdeutigkeiten gibt.
Martin Smith
PostgreSQL zeigt die gleichen Ergebnisse wie SQLServer, und soweit ich aus der Ausgabe von explain"Result (rows = 1) ..." für "Seq Scan" für "WHERE" ersehen kann, wird auch nicht in die Tabelle geschaut. Ich denke, es hängt irgendwie damit zusammen, dass "FROM" in TSQL und PostgreSQL nicht obligatorisch ist. Ich weiß, dass Mysql es auch nicht benötigt, aber da sie es unterstützen dual, analysieren sie die Abfrage wahrscheinlich ein bisschen anders. Ich stimme zu, es klingt wie eine Spekulation, aber ich hoffe, dass es einen Sinn ergibt.
a1ex07,