dedlfix: Geburtstage der letzten und kommenden 3 Tage

Beitrag lesen

Tach!

[HAVING] sollte man nicht als WHERE-Ersatz missbrauchen, weil das unnötig Ressourcen verbraucht (wenn es der Optimizer nicht wegoptimiert).
Das verstehe ich nicht so ganz. Was wäre denn ein _simples_ Beispiel für so einen unötigen WHERE-Ersatz? Anders gefragt: Kann man den Unterschied/Verwendungszweck bei WHERE und HAVING irgendwie verständlich erklären?

Statt Beispiel formuliere ich das mal so: HAVING dient dazu, ein Ergebnis zu filtern, das erst nach dem Gruppieren entsteht. Das geht im WHERE ja noch nicht. Alles andere kann im WHERE gefiltert werden. Ein HAVING-"Missbrauch" liegt also dann vor, wenn man es ohne Gruppierung verwendet.

Das heißt, im WHERE kann man auf Aliasnamen (und Berechnungsergebnisse) für Tabellen zugreifen und nicht auf die im SELECT.
Also das ist mir jetzt _völlig_ unverständlich! Was meinst Du mit "Aliasnamen (und Berechnungsergebnisse) für Tabellen"?

Aliasnamen für Tabellen sollte klar sein. Berechnungsergebnisse sollte hier eine Subquery sein, die eine Ergebnismenge ähnlich einer Tabelle liefert.

SELECT preis, (preis * 2) AS doppelter_preis FROM katalog

Hier ist "doppelter_preis" ein Aliasname einer virtuellen Spalte. Also einer Spalte, die es in der Datenbank eigentlich gar nicht real gibt. Gefüllt mit berechneten Werten. Der Aliasnahme entstammt dem SELECT, kann also, wie wir vorher festgestellt haben, nur im HAVING und im ORDER BY verwendet werden. Und was wäre im Gegensatz dazu jetzt ein "Aliasnamen (und Berechnungsergebnis) für Tabellen", auf den ich laut Deinem Satz auch im WHERE zugreifen kann?

SELECT preis, (preis * 2) AS doppelter_preis FROM katalog k WHERE k.produktgruppe = 'foo'

In dem Beispiel ist der Tabellenalias k verzichtbar. Interessanter wird er erst wenn mehrere Tabellen gejoint werden (und man nicht immer den kompletten Tabellennamen hinschreiben will).

SELECT * FROM table1 t1 LEFT JOIN (SELECT * FROM table2) t2 WHERE t2.id IS NULL

Das ist ein Beispiel für eine Subquery. Hier ist die Subquery zwar nicht sinnvoll, weil sie direkt durch den table2 ersetzt werden kann, aber du musst dir vorstellen, dass das da eine komplexe Abfrage ist, die man nicht über Join- und Where-Bedingungen abbilden kann. Eine Subquery im FROM jedenfalls verlangt immer einen Alias, auch wenn man ihn später nicht verwendet.

Mal angenommen, du hast keine Berechnung sondern einen einfachen Vergleich im WHERE, dann kann unter Umständen ein Index zum schnelleren Finden der Datensätze verwendet werden. Sortierst du erst im HAVING die ungewünschten Datensätze weg, dann arbeitest du bereits mit den Daten der Ergebnismenge und nicht mehr mit den Daten aus der Tabelle. Dann hat das SELECT vielleicht viele Berechnungen (für die anderen Felder) umsonst angestellt, deren Datensätze das WHERE gar nicht erst durchgelassen hätte.

Ich bilde mir zwar ein, das zumindest teilweise zu verstehen, wenn ich dabei auf die von Dir gepostete Avarbeitungsfolge schaue, aber so ganz klar ist mir die Sache nicht. Kannst Du das evt. an Hand eines ganz trivialen Beispiels verständlich machen?

SELECT * FROM foo WHERE id = ?

Ein Index auf id kann verwendet werden.

SELECT * FROM foo WHERE a + b = ?  
SELECT * FROM foo WHERE bar(a) = ?

Es gibt keine berechneten Indexe in MySQL, also muss hier ein Full-Table-Scan ausgeführt werden, weil erst nach dem Berechnen von a+b feststeht, ob der Datensatz selektiert werden soll. Dasselbe gilt für bar(a), das muss auch erst für jeden Datensatz einzeln berechnet werden.

Bei Datumsfilterungen ist es immer von Vorteil, wenn man das Datumsfeld direkt angeben kann und nicht mit einer Funktion darauf zugreift. Das geht natürlich nicht, wenn man nur Teile vom Datum braucht und das Selektionskriterium nicht auf vollständige Datümer umformulieren kann.

Ich weiß nicht, ob MySQL bei einem HAVING im Missbrauchsfall einen Index verwendet, bei "ordentlicher" Verwendung nach einem GROUP BY auf ein Aggregationsergebnis ist das jedenfalls nicht möglich.

Es gibt eine deutlich einfachere, wenn man den 29. Februar außen vor lässt. DAYOFYEAR() spart eine ganze Menge Funktionsaufrufe.
[...] Nein, das DAYOFYEAR() bringt in diesem Fall nichts.

... wenn die Lösung rein datenbankbasiert sein soll.

dedlfix.