Ben: Geburtstag und -monat aus MySQL auslesen

Hallo liebe Forenmitglieder,

für eine Adressliste möchte ich die Einträge aller Personen auslesen, die in den nächsten sieben Tagen Geburtstag haben.

Die Geburtstage sind als DATE abgespeichert und daher erfolgt das Auslesen derzeit nach folgendem Schema:
SELECT * FROM nutzer WHERE (RIGHT(geb,5) >= RIGHT(CURDATE(),5)) AND (RIGHT(geb,5) < RIGHT(DATE_ADD(CURDATE(), INTERVAL 7 DAY),5));

Leider scheitert diese Methode jedoch an den Jahreswechseln, weshalb ich dies gerne anderweitig lösen würde. Auch die Abfrage über BETWEEN ist für normale Datumsangaben prinzipiell recht einfach, doch wie setze ich es um wenn jede Zeile ein anderes Jahr enthält?

Besten Dank im Voraus,
Ben

  1. doch wie setze ich es um wenn jede Zeile ein anderes Jahr enthält?

    Du könntest z.B. nur DAYOFYEAR() vergleichen - damit hast du nur am Jahreswechsel 7 Tage lang ein Problem mit dieser Methode - das könntest du damit kompensieren, indem du die zu berechendende Tagesspanne mit einbeziehst und dazurechnest.

  2. Ich habe nur mäßige Erfahrung mit Datums-Werten, aber in der MySQL-Doku steht wörtlich:

    Es folgt ein Beispiel, welches Datumsfunktionen verwendet. Die folgende
    Abfrage wählt alle Datensätze aus, die einen date_col-Wert haben, der
    in den letzten 30 Tagen liegt:

    SELECT something FROM tbl_name  
    WHERE DATE_SUB(CURDATE(),INTERVAL 30 DAY) <= date_col;
    

    Scheint mir genau das zu sein, was du suchst.

    Ansonsten wäre mein Ansatz UNIX_TIMESTAMP()
    WHERE (UNIX_TIMESTAMP(geb) = UNIX_TIMESTAMP(now()));Oder so

    --
    sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
    1. WHERE (UNIX_TIMESTAMP(geb) = UNIX_TIMESTAMP(now()));Oder so

      Quatsch "gleich" dann natürlich mit BETWEEN now() und now()+7*24*60*60

      --
      sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
    2. Hi,

      Ich habe nur mäßige Erfahrung mit Datums-Werten, aber in der MySQL-Doku steht wörtlich:

      Es folgt ein Beispiel, welches Datumsfunktionen verwendet. Die folgende
      Abfrage wählt alle Datensätze aus, die einen date_col-Wert haben, der
      in den letzten 30 Tagen liegt

      Mein Geburtsdatum liegt aber nicht innerhalb der letzten 30 Tage - andernfalls wäre ich vermutlich eher nicht in der Lage, dir hier zu Antworten. (Und wenn doch, dann wäre es vermutlich eher sowas wie "hihi glucks gagga, ra-bäähäh!")

      MfG ChrisB

      --
      Light travels faster than sound - that's why most people appear bright until you hear them speak.
      1. Mein Geburtsdatum liegt aber nicht innerhalb der letzten 30 Tage - andernfalls wäre ich vermutlich eher nicht in der Lage, dir hier zu Antworten. (Und wenn doch, dann wäre es vermutlich eher sowas wie "hihi glucks gagga, ra-bäähäh!")

        Ääh fürwahr, ich sollte sieben Sekunden länger denken bevor ich antworte.

        --
        sh:( fo:| ch:? rl:( br:& n4:& ie:{ mo:} va:) de:µ_de:] zu:) fl:( ss:| ls:[ js:(
    3. Hallo,

      Ansonsten wäre mein Ansatz UNIX_TIMESTAMP()
      WHERE (UNIX_TIMESTAMP(geb) = UNIX_TIMESTAMP(now()));Oder so

      UNIX_TIMESTAMP() ist für viele relevanten Geburtsdaten, zum Beispiel mein eigenes, ungeeignet. Ich rate von der Verwendung von Timestamps für solche Datumsangaben dringendst ab. Wozu gibt es ordentliche Datumsdatentypen?

      Freundliche Grüße

      Vinzenz

  3. Hello,

    Leider scheitert diese Methode jedoch an den Jahreswechseln, weshalb ich dies gerne anderweitig lösen würde. Auch die Abfrage über BETWEEN ist für normale Datumsangaben prinzipiell recht einfach, doch wie setze ich es um wenn jede Zeile ein anderes Jahr enthält?

    Du kannst das Vergleichsdatum doch zusammensetzen aus

    aktuelles Jahr, Monat des Geburtstages, Tag des Geburtstages

    Und das kannst Du dann in Deine Between-Bedingung einsetzen.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hello,

      Du kannst das Vergleichsdatum doch zusammensetzen aus

      aktuelles Jahr, Monat des Geburtstages, Tag des Geburtstages

      Und das kannst Du dann in Deine Between-Bedingung einsetzen.

        
        
      select * from zeit  
      where datediff  
            (  
                date_add(now(), interval 7 day),  
                concat(year(now()),'-',month(birthday),'-',day(birthday))  
            )  
            between 0 and 7;  
        
      
      

      so geht es.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
      Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Hallo Tom,

        select * from zeit
        where datediff
              (
                  date_add(now(), interval 7 day),
                  concat(year(now()),'-',month(birthday),'-',day(birthday))
              )
              between 0 and 7;

        
        > so geht es.  
          
        nein, so geht es nicht immer. [suit](https://forum.selfhtml.org/?t=186764&m=1240651) hatte bereits auf den Jahreswechsel hingewiesen, den Dein Code nicht berücksichtigt. suit vergaß allerdings auf die Schaltjahrproblematik hinzuweisen, die bei der Verwendung von DAYOFYEAR zusätzlich berücksichtigt werden muss.  
          
        Dein Code vernachlässigt die Schaltjahresproblematik in der Form, dass Personen nicht am 29. Februar geboren sein dürfen :-)  
          
        ~~~sql
        SELECT  
            DATEDIFF(  
                DATE_ADD(NOW(), INTERVAL 7 DAY),  
                '2009-02-28'  
            ) differenz
        

        liefert mir 82 als Wert,

          
        SELECT  
            DATEDIFF(  
                DATE_ADD(NOW(), INTERVAL 7 DAY),  
                '2009-02-29'  
            ) differenz
        

        jedoch NULL.

        Freundliche Grüße

        Vinzenz

        1. Hello Vinzenz,

          select * from zeit
          where datediff
                 (
                     date_add(now(), interval 7 day),
                     concat(year(now()),'-',month(birthday),'-',day(birthday))
                 )
                 between 0 and 7;

          
          > > so geht es.  
          >   
          > nein, so geht es nicht immer. [suit](https://forum.selfhtml.org/?t=186764&m=1240651) hatte bereits auf den Jahreswechsel hingewiesen, den Dein Code nicht berücksichtigt. suit vergaß allerdings auf die Schaltjahrproblematik hinzuweisen, die bei der Verwendung von DAYOFYEAR zusätzlich berücksichtigt werden muss.  
            
          Stimmt. Ertappt. Dabei habe ich wohl im Kreis gedacht.  
          Aber der betrachtete Bereich verlässt ja den gültigen für year(now())...  
          Ok, das könnte man einfach fixen, indem man das nächste Jahr auch abfragt. Da sich diese beiden Bereiche nicht überschneiden, dürfte es passen.  
            
          Und für den 29. Februar, der mMn nach als amtlicher Geburtstag nicht angegeben wird, müsste wohl ein if() her?  
            
          ~~~sql
            
            
           select * from zeit  
           where datediff  
                 (  
                     date_add(now(), interval 7 day),  
                     concat(year(now()),'-',month(birthday),'-',if(month(birthday) = 2 and day(birthday)= 29,28,day(birthday))  
                 )  
                 between 0 and 7  
            
           or    datediff  
                 (  
                     date_add(now(), interval 7 day),  
                     concat((year(now())+1),'-',month(birthday),'-',if(month(birthday) = 2 and day(birthday)= 29,28,day(birthday))  
                 )  
                 between 0 and 7  
          ;  
            
          
          

          Dein Code vernachlässigt die Schaltjahresproblematik in der Form, dass Personen nicht am 29. Februar geboren sein dürfen :-)

          der 29.02.2009 lässt sich gar nicht eintragen in die DB

          mysql> insert into zeit set name="Vierauge", birthday='2009-02-29';
          Query OK, 1 row affected, 1 warning (0.01 sec)

          mysql> select * from zeit;
          +----+------------+------------+
          | id | name       | birthday   |
          +----+------------+------------+
          |  1 | NULL       | 1958-02-08 |
          |  2 | Margret    | 1933-01-07 |
          |  3 | Wolfgang   | 1930-08-25 |
          |  4 | Elke       | 1964-12-03 |
          |  5 | Louisa     | 1984-01-15 |
          |  6 | Thomas     | 1958-02-08 |
          |  7 | Paul       | 1960-05-20 |
          |  8 | Paula      | 1970-05-21 |
          |  9 | Paulinchen | 1970-05-12 |
          | 10 | Vierauge   | 0000-00-00 |
          +----+------------+------------+
          10 rows in set (0.00 sec)

          während sich für ein Jahr, in dem es einen 29. Februar gibt, dieser auch angenommen wird. MySQL ist also auch hier inzwischen etwas schlauer geworden.

          mysql> insert into zeit set name="Willi-2008", birthday='2008-02-29';
          Query OK, 1 row affected (0.00 sec)

          mysql> select * from zeit;
          +----+------------+------------+
          | id | name       | birthday   |
          +----+------------+------------+
          |  1 | NULL       | 1958-02-08 |
          |  2 | Margret    | 1933-01-07 |
          |  3 | Wolfgang   | 1930-08-25 |
          |  4 | Elke       | 1964-12-03 |
          |  5 | Louisa     | 1984-01-15 |
          |  6 | Thomas     | 1958-02-08 |
          |  7 | Paul       | 1960-05-20 |
          |  8 | Paula      | 1970-05-21 |
          |  9 | Paulinchen | 1970-05-12 |
          | 10 | Vierauge   | 0000-00-00 |
          | 11 | Willi-2008 | 2008-02-29 |
          +----+------------+------------+
          11 rows in set (0.00 sec)

          Der Code sollte jetzt nur eine Anregung sein, für den Fall, dass man den 29.02.2009 tatsächlich berücksichtigen müsste. Da diese Datum aber bei MySQL 5.0.51a (bei mir hier noch verwendet) gar nicht mehr eingetragen werden kann, sollte es also ohne zusätzliche Bedingungen gehen.

          Vermutlich müsste der Code er für eine "amtliche" Geburtstagswoche sowieso noch komplizierter werden, weil die Geburtstagslinder in DE vom 29. Februar auf den ersten März verfrachtet werden.

          Man müsste also auch erst noch etwas über die lokalen Gesetzmässigleiten wissen.

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. Hallo Tom,

            Und für den 29. Februar, der mMn nach als amtlicher Geburtstag nicht angegeben wird, müsste wohl ein if() her?

            selbstverständlich steht bei Personen, die am 29. Februar eines Schaltjahres geboren werden, das korrekte Geburtsdatum, der 29. Februar im Ausweis und allen Papieren. Warum sollten dort Angaben verfälscht werden?

            Freundliche Grüße

            Vinzenz

          2. Hallo Tom,

            select * from zeit
            where datediff
                   (
                       date_add(now(), interval 7 day),
                       concat(year(now()),'-',month(birthday),'-',if(month(birthday) = 2 and day(birthday)= 29,28,day(birthday))
                   )
                   between 0 and 7

            or    datediff
                   (
                       date_add(now(), interval 7 day),
                       concat((year(now())+1),'-',month(birthday),'-',if(month(birthday) = 2 and day(birthday)= 29,28,day(birthday))
                   )
                   between 0 and 7
            ;

              
            nein, das berücksichtigt den Schalttag immer noch nicht korrekt:  
            Am 21. Februar eines Schaltjahres behauptet Dein Statement, eine am 29. Februar eines anderen Schaltjahres geborene Person habe innerhalb der nächsten 7 Tage Geburtstag.  
              
              
            Freundliche Grüße  
              
            Vinzenz
            
            1. Hello,

              stimmt sowieso immer noch nicht.
              Die Korrektur der letzten Tage (hier 7) muss ja auf jeden Fall stattfinden...

              Nun suche ich aber erstmal, warum ich diese Fehlermeldung bekomme.
              Die war ja vorhin nicht da.

              ERROR 1305 (42000): FUNCTION test.datediff does not exist

              Liebe Grüße aus dem schönen Oberharz

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
          3. Hello Vinzenz,

            nun dachte ich, so müsste es funktionieren, aber nun bekomme ich die Fehlermeldung

            ERROR 1305 (42000): FUNCTION test.datediff does not exist

            [code lang=sql]

            select * from zeit
             where datediff
                   (
                       date_add(now(), interval 7 day),
                       if(
                             month(birthday) = 2 and day(birthday)= 29 and DAYOFYEAR(CONCAT(YEAR(NOW()),'-12-31')) = 365,
                             concat(YEAR(NOW()),'-03-01'),
                             if(
                                   month(now()) = 12 and day(now()) > (31-7),
                                   concat((YEAR(NOW())+1),'-',month(birthday),'-',day(birthday)),
                                   concat(YEAR(NOW()),'-',month(birthday),'-',day(birthday))
                               )
                         )
                   )
                   between 0 and 7
             ;

            Was habe ich denn nun übersehen?

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
            Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Hello,

              Ok, letzter Anlauf.

              Der Fehler rührte daher, weil MySQL keine Leerzeichen (oder Whitespaces) zwischen dem Funktionsnamen  und der öffnenden Klammer duldet.

              Die Abfrage müsste mMn nun aber so benutztbar sein, wenn sie auch recht unhandlich geworden ist. Ich würde auf jeden Fall eine benutzerdefinierte Funktion daraus machen.

              select * from zeit
              where datediff(
                     date_add(now(), interval 7 day),
                     if(
                           month(birthday) = 2 and day(birthday)= 29 and DAYOFYEAR(CONCAT(YEAR(NOW()),'-12-31')) = 365,
                           concat(YEAR(NOW()),'-03-01'),
                           if(
                                 month(now()) = 12 and day(now()) > (31-7),
                                 concat((YEAR(NOW())+1),'-',month(birthday),'-',day(birthday)),
                                 concat(YEAR(NOW()),'-',month(birthday),'-',day(birthday))
                             )
                       )
                    )
                    between 0 and 7
              ;

              Wenn der Geburtstag auf einen 29. Februar fällt und das Jahr kein Schaltjahr ist, muss der Geburtstag korrigiert werden auf den (in DE) 01.03.
              Da er aber nun auf den 01.03. fällt, muss nicht geprüft werden, ob er in die letzte Woche des Jahres fällt (da war meine boolesche Algebra also doch nicht für den A....). Wenn es sich aber um ein gültiges Datum handelt (also auch ein 29. Februar in einem Schaltjahr) regelt MySQL alles, bis auf die letzte Woche. Die muss nun noch kontrolliert werden. Der Stichtag dann ins nächste Jahr verschoben.

              Jetzt muss nur noch überlegt werden, ob 8 (0 bis 7) oder 7 Tage betrachtet werden sollen.

              Liebe Grüße aus dem schönen Oberharz

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de