Tach!
(Ich gehe davon aus, dass durch eine Eingabevalidierung sichergestellt ist, dass in der Geburtstagsspalte ein Datum aus der Vergangenheit stehen _muss_!)
(Ist im Prinzip egal, wenn es nur um die Geburtstagsanzeige und zum Beispiel keine rechtlich relevanten Dinge geht. Dann wird eben zum minus soundosovielten Geburtstag gratuliert.)
Hier stehe ich leider vor dem Problem, dass ich auch selbst nach stundenlangem Nachdenken den Block […]AS
days\_to\_birthday
nicht mal ansatzweise verstehe.
Nach AS steht ein Aliasname, der zum Beispiel als "Feldname" in der Ergebnismenge auftaucht. Ansonsten steht da nämlich exakt das was im SELECT steht: ein Feldname oder der komplette Ausdruck.
Was mir hier nicht gefällt ist, dass eine virtuelle Spalte „BirthdayInCurrentYear“ angelegt wird aber dann nicht mit dierser Spalte gearbeitet wird, sondern die Berechnung für die Spaltenwerte ein 2. Mal im Query steht.
Der Aliasname wird nach der Berechnung vergeben und im WHERE ist er noch gar nicht vorhanden. Es gibt eine Reihenfolge, in der die Klauseln ausgewertet werden. Aliasnamen sind immer erst nach dem Abarbeiten der jeweiligen Klausel für die anderen Klauseln verfügbar. Aliasnamen im SELECT können im ORDER BY verwendet werden. Im HAVING auch, aber das sollte man nicht als WHERE-Ersatz missbrauchen, weil das unnötig Ressourcen verbraucht (wenn es der Optimizer nicht wegoptimiert).
FROM -> WHERE -> GROUP BY -> SELECT -> HAVING -> ORDER BY -> LIMIT
Das SELECT steht üblicherweise vorn, schmuggelt sich aber in der Abarbeitungsreihenfolge dazwischen. Ansonsten ist die Reihenfolge so, wie man die Klauseln notieren muss. Das heißt, im WHERE kann man auf Aliasnamen (und Berechnungsergebnisse) für Tabellen zugreifen und nicht auf die im SELECT.
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.
Weiterhin definiert der SQL-Standard, dass HAVING sich nur auf GROUP-BY-Spalten und Aggregatfunktionen beziehen darf. MySQL ist etwas freier und gestattet das HAVING auch ohne die Anwendung von GROUP BY. Woanders bekommst du sowas als Fehler abgelehnt.
2.) Wieso greift NOW() + INTERVAL 3 DAY für die kommenden 3 Tage, aber NOW() - INTERVAL 3 DAY nur für die letzten 2 Tage?
SELECT NOW() - INTERVAL 3 DAY lieferte eben 2013-08-30 18:57:28. Das ist größer als der 2013-08-30 (00:00:00) und somit ist der 30.8. nicht mehr im Intervall.
4.) Was haltet Ihr generell von meiner Lösung? Ist das OK so oder völlig unbrauchbar? Ist das erste verlinkte Beispiel besser und wenn ja, warum?
Es gibt eine deutlich einfachere, wenn man den 29. Februar außen vor lässt. DAYOFYEAR() spart eine ganze Menge Funktionsaufrufe.
Ob ein Schaltjahr ist, lässt sich mit MySQL nicht einfach herausfinden. Deswegen würde ich Schaltjahre ignorieren (dann ist da eben ein Versatz um einen Tag drin) oder sie außerhalb SQLs berücksichtigen, in der Form, dass ich die Tagesnummern für die obere und untere Grenze in die Query reinreiche und nicht in MySQL ermittle.
5.) [...] Unter anderem verstehe ich die Stelle mit dem „ , 1, 0))“ nicht.
Die relevante Stelle sieht gekürzt so aus:
... + IF(x > y, 1, 0)
1 und 0 sind Parameter der IF-Funktion. Das IF() liefert abhängig vom Vergleichsergebnis eine 1 oder 0 und die wird zum vorhergehenden Teilausdruck addiert.
dedlfix.