SQL - mit VS wo


202

Ich habe die folgenden zwei Tabellen:

1. Lecturers (LectID, Fname, Lname, degree).
2. Lecturers_Specialization (LectID, Expertise).

Ich möchte den Dozenten mit der größten Spezialisierung finden. Wenn ich das versuche, funktioniert es nicht:

SELECT
  L.LectID, 
  Fname, 
  Lname 
FROM Lecturers L, 
     Lecturers_Specialization S
WHERE L.LectID = S.LectID
AND COUNT(S.Expertise) >= ALL (SELECT
  COUNT(Expertise)
FROM Lecturers_Specialization
GROUP BY LectID);

Aber wenn ich das versuche, funktioniert es:

SELECT
  L.LectID,
  Fname,
  Lname 
FROM Lecturers L,
     Lecturers_Specialization S
WHERE L.LectID = S.LectID
GROUP BY L.LectID,
         Fname,
         Lname 
HAVING COUNT(S.Expertise) >= ALL (SELECT
  COUNT(Expertise)
FROM Lecturers_Specialization
GROUP BY LectID); 

Was ist der Grund? Vielen Dank.


2
Können Sie klären, welche SQL-Version Sie verwenden (MySQL, MS SQL, PostgreSQL, Oracle usw.)? Wenn Sie sagen "funktioniert nicht", meinen Sie damit, dass die Ergebnisse nicht Ihren Erwartungen entsprechen oder dass ein Kompilierungs- / Analysefehler vorliegt?
jklemmack

2
Warum verwenden Sie ALL anstelle von MAX?. Gibt es einen Vorteil?
Skan

Antworten:


351

WHEREKlausel führt eine Bedingung für einzelne Zeilen ein ; HAVINGKlausel führt eine Bedingung für Aggregationen ein , dh Ergebnisse der Auswahl, bei denen ein einzelnes Ergebnis wie Anzahl, Durchschnitt, Min, Max oder Summe aus mehreren Zeilen erzeugt wurde. Ihre Abfrage erfordert eine zweite Art von Bedingung (dh eine Bedingung für eine Aggregation) und HAVINGfunktioniert daher ordnungsgemäß.

Als Faustregel verwenden Sie WHEREvorher GROUP BYund HAVINGnachher GROUP BY. Es ist eine eher primitive Regel, aber in mehr als 90% der Fälle nützlich.

Wenn Sie schon dabei sind, möchten Sie Ihre Abfrage möglicherweise mithilfe der ANSI-Version des Joins neu schreiben:

SELECT  L.LectID, Fname, Lname
FROM Lecturers L
JOIN Lecturers_Specialization S ON L.LectID=S.LectID
GROUP BY L.LectID, Fname, Lname
HAVING COUNT(S.Expertise)>=ALL
(SELECT COUNT(Expertise) FROM Lecturers_Specialization GROUP BY LectID)

Dies würde beseitigen, WHEREdass als Theta-Verbindungsbedingung verwendet wurde .


39

HAVINGarbeitet mit Aggregaten. Da COUNTes sich um eine Aggregatfunktion handelt, können Sie sie nicht in einer WHEREKlausel verwenden.

Hier finden Sie einige Informationen aus MSDN zu Aggregatfunktionen.


30

Zuerst sollten wir die Reihenfolge der Ausführung von Klauseln kennen, dh FROM> WHERE> GROUP BY> HAVING> DISTINCT> SELECT> ORDER BY. Da die WHERE- Klausel vor der GROUP BY- Klausel ausgeführt wird, können die Datensätze nicht gefiltert werden, indem WHERE auf einen GROUP BY- angewendeten Datensatz angewendet wird.

"HAVING ist dasselbe wie die WHERE-Klausel, wird jedoch auf gruppierte Datensätze angewendet".

Zuerst ruft die WHERE- Klausel die Datensätze basierend auf der Bedingung ab, dann gruppiert die GROUP BY- Klausel sie entsprechend und dann ruft die HAVING- Klausel die Gruppendatensätze basierend auf der vorhandenen Bedingung ab.


Wird diese Reihenfolge immer verwendet? Was passiert, wenn der Abfrageoptimierer die Reihenfolge ändert?
MSIS

1
@MSIS Selbst wenn der Abfrageoptimierer die Reihenfolge ändert, sollte das Ergebnis das gleiche sein, als ob diese Reihenfolge eingehalten wurde. Es ist eine logische Reihenfolge.
Stephen

18
  1. Die WHERE-Klausel kann mit den Anweisungen SELECT, INSERT und UPDATE verwendet werden, während die HAVING-Anweisung nur mit der Anweisung SELECT verwendet werden kann.

  2. WHERE filtert Zeilen vor der Aggregation (GROUP BY), während HAVING Filtergruppen nach der Aggregation durchgeführt werden.

  3. Die Aggregatfunktion kann in der WHERE-Klausel nur verwendet werden, wenn sie sich in einer in der HAVING-Klausel enthaltenen Unterabfrage befindet, während die Aggregatfunktionen in der HAVING-Klausel verwendet werden können.

Quelle


11

Ich habe kein Beispiel für beide in einer Abfrage gesehen. Dieses Beispiel könnte also helfen.

  /**
INTERNATIONAL_ORDERS - table of orders by company by location by day
companyId, country, city, total, date
**/

SELECT country, city, sum(total) totalCityOrders 
FROM INTERNATIONAL_ORDERS with (nolock)
WHERE companyId = 884501253109
GROUP BY country, city
HAVING country = 'MX'
ORDER BY sum(total) DESC

Dadurch wird die Tabelle zuerst nach der Unternehmens-ID gefiltert, dann (nach Land und Stadt) gruppiert und zusätzlich nur nach Stadtaggregationen in Mexiko gefiltert. Die Unternehmens-ID wurde in der Aggregation nicht benötigt, aber wir konnten WHERE verwenden, um nur die gewünschten Zeilen herauszufiltern, bevor wir GROUP BY verwenden.


Es ist kein gutes Beispiel, da Sie Folgendes konvertieren könnten: "WHERE companyId = 884501253109 GRUPPE NACH Land, Stadt MIT LAND =" MX "" in: "WHERE companyId = 884501253109, country =" MX "GRUPPE NACH Stadt"
Etienne Herlaut

Wenn Sie nur die [country] -Filterung in das von Ihnen vorgeschlagene WHERE verschieben, wird die Abfrage von SELECT [country] fehlgeschlagen, da [country] nicht mehr in der GROUP BY-Aggregation enthalten ist und daher nicht ausgewählt werden kann.
Nhan

Ihr Punkt zur Optimierung liegt darin, [Land] nach WHERE zu verschieben, da dies ein kleinerer Datensatz wäre, mit dem später GROUP BY erstellt werden soll. Dies ist natürlich nur ein Beispiel, um mögliche Verwendungen zu veranschaulichen. Wir können zu HAVING sum (total)> 1000 wechseln, und das wäre ein vollständig gültiger Fall, um WHERE und HAVING einzuschließen.
Nhan

9

Sie können die where-Klausel nicht mit Aggregatfunktionen verwenden, da sie beim Abrufen von Datensätzen auf der Grundlage der Bedingung Datensatz für Datensatz in die Tabelle eingeht und dann den Datensatz auf der Grundlage der von uns angegebenen Bedingung abruft. Also diesmal können wir nicht wo Klausel. Während die Klausel auf dem resultSet funktioniert, das wir schließlich nach dem Ausführen einer Abfrage erhalten.

Beispielabfrage:

select empName, sum(Bonus) 
from employees 
order by empName 
having sum(Bonus) > 5000;

Dadurch wird das resultSet in einem temporären Speicher gespeichert, und die Klausel wird ihre Arbeit ausführen. Daher können wir hier problemlos Aggregatfunktionen verwenden.


2
Ich denke, wir können die HAVING-Klausel nicht ohne die GROUP BY-Klausel verwenden. Position der HAVING-Klausel - SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY
Morez

4

1. Wir können die Aggregatfunktion mit der HAVING-Klausel verwenden, nicht mit der WHERE-Klausel, z. B. min, max, avg.

2. Die WHERE-Klausel entfernt das Datensatztupel für Tupel. Die HAVING-Klausel entfernt die gesamte Gruppe aus der Sammlung der Gruppe

Meistens wird HAVING verwendet, wenn Sie Datengruppen haben, und WHERE wird verwendet, wenn Sie Daten in Zeilen haben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.