Achot: Datum +6 Wochen

Hallo,

eine Frage ist es möglich direkt im SELECT zu sagen rechne auf ein Datum +Wochen, welches in drei verschieden Spalten liegt?

function mhd($mysqli) {
        
        $stmt = $mysqli->prepare("SELECT id, titel FROM artikel");
        $stmt->execute();
        $stmt->bind_result($id, $titel);
        $stmt->store_result();
        
        if($stmt->num_rows() >  0) {     
            
            while ($stmt->fetch()){
            
                $mhd[] = array( 
                    'id'      => $id,
                    'titel'   => $titel
                );
            }
            return $mhd;
            }
    }

Das Datum liegt in den Spalten

  • haltbar_bis_jahr
  • haltbar_bis_monat
  • haltbar_bis_tag

Also irgendwie so etwas?

WHERE DATE( FROM_UNIXTIME( XXXXX ) ) > NOW() + INTERVAL 6 WEEK"
  1. eine Frage ist es möglich direkt im SELECT zu sagen rechne auf ein Datum +Wochen, welches in drei verschieden Spalten liegt?

    Das Datum liegt in den Spalten

    • haltbar_bis_jahr
    • haltbar_bis_monat
    • haltbar_bis_tag

    Sofern die Produkte nicht jeden Monat aufs Neue wieder vier Wochen haltbar sind, erscheint mir diese Aufteilung hanebüchen. Ein Datum besteht aus Tag, Monat und Jahr, anders ergeben die Angaben keinen Sinn. Wann möchtest du denn nach Produkten suchen, die "im Dezember" oder "am 13." verfallen, die verfallen "im Dezember 2017" oder "am 13. Juni 2018".

    In der Folge bekommst du Probleme beim Umgang mit den Daten – genau wie jetzt gerade. Wäre das Datum als ein Datum (ein Objekt) gespeichert, könntest du einfach "where haltbar_bis > now() + interval 6 week" angeben, anstatt jetzt Klimmzüge machen zu müssen.

    Aber davon ab: Schaue in der MySQL-Anleitung, ob es dort einen date()-Befehl gibt, der Einzelteile eines Datums annimmt.

    WHERE DATE(haltbar_bis_jahr, haltbar_bis_monat, haltbar_bis_tag) > NOW() + INTERVAL 6 WEEK
    

    (Trotzdem mein Rat, die drei Spalten zu einer Spalte vom Typ Datum zusammenfassen.)

    1. Danke für deine Hilfe. Ich weiß dass die Daten nicht immer so geliefert werden wie ich diese benötige. Ich habe derzeit 4 verschiedene Lieferanten von Daten (Locations, Events, Tickets, Wetter) Jeder bringt ein anderes Format mit. Insgesamt arbeiten an der Seite 4 Menschen, die dieses nebenberuflich machen. Wir haben uns daher vor Monaten entschieden die Daten so zu nehmen, wie sie kommen. Oftmals aus einer Excel-Liste und dort ist es üblich das Datum aufzusplitten um danach suchen zu können.

      Ich könnte natürlich ein weiteres Feld einfügen und das Datum intern zusammen zu setzten. Welches Formal wäre denn am besten?

      • 12.03.2017
      • 12-03-2017
      • 2017.03.12
      • 2017-03-12

      Oftmals möchte ich z.B. nur nach Jahr suchen oder nach einem Monat oder zeig mir alle Einträge aus 2016 12 an. Da ist so eine Aufteilung schon etwas schönes?

      1. Ich weiß dass die Daten nicht immer so geliefert werden wie ich diese benötige.

        Dann würde ich sie mir gleich bei Übernahme so hin rechnen wie ich sie brauche.

        Jeder bringt ein anderes Format mit.

        Ein Grund mehr das zu tun, du brauchst ja eine einheitliche Grundlage für dein System.

        Denke darüber nach was du wie suchen willst und ob ein Datumsfeld das nicht auch kann.

        Welches Formal wäre denn am besten?

        Das Datumsformat der Datenbank ;-) Die Formate die du zeigst sind Strings für die Darstellung. Die ist ein Unterschied zur Speicherung.

        Oftmals möchte ich z.B. nur nach Jahr suchen oder nach einem Monat oder zeig mir alle Einträge aus 2016 12 an. Da ist so eine Aufteilung schon etwas schönes?

        Da ist ein Datumsformat was schönes, mit dem du der Datenbank sagen kannst "bitte alles von 2016-11-05 bis 2017-03-25".

        Auch deine DB speichert sowas sehr wahrscheinlich als Zahl, Sekunden seit x oder Tage seit x. Da lässt sich dann leicht berechnen was 3 Wochen später wäre, nämlich einfach plus 42 Tage.

        1. Noch ein Nachtrag, hab ich vergessen zu sagen.

          Es gibt etliche verschiedene Stringformat für Datumswerte. USA nutzt Monat.Tag.Jahr (oder mit -?). Wir haben Tag.Monat.Jahr. Jemand anderes hat wieder was sonstiges.
          Nutze um der Datenbank ein Datum als String zu geben oder eins zu lesen auf jeden Fall ein allgemeines Format, wie Jahr-Monat-Tag. Nimm NIE ein länderspezifisches Format und lasse die Datenbank das umrechnen. Dein System mit der DB steht vielleicht heute auf Deutsch und macht aus 4.2.2017 den 4. Februar. Irgendwann steht sie auf USA und interpretiert in diesen String den 2. April - und dein Systen ist nutzlos.

          Dazu sagt dir eine Anleitung zu deiner DB sicher ob sie ein allgemeingültiges Datumsformat hat das nicht lokalisiert wird. Nutze das, dann hast du am wenigsten Ärger.

        2. Hallo,

          Da lässt sich dann leicht berechnen was 3 Wochen später wäre, nämlich einfach plus 42 Tage.

          Hast du auch manchmal das Gefühl, dass die Woche einfach kein Ende nehmen will?

          Gruß
          Kalk

          1. Durchaus!
            Ich weiß gerade nicht warum ich 3 geschrieben habe. Es ist definitiv Zeit für den Mittagsschlaf.

          2. Hi,

            Da lässt sich dann leicht berechnen was 3 Wochen später wäre, nämlich einfach plus 42 Tage.

            Hast du auch manchmal das Gefühl, dass die Woche einfach kein Ende nehmen will?

            Jeden Montag morgen um ca. 9 Uhr überkommt mich dieses Gefühl. Ausnahme: Oster- und Pfingstmontag. 😉

            cu,
            Andreas a/k/a MudGuard

          3. Hast du auch manchmal das Gefühl, dass die Woche einfach kein Ende nehmen will?

            Hatte neulich das Gegenteil. Kurz gesagt gab es aus "außenliegenden" Gründen eine Verschiebung des Beginns und des Endes meiner Tätigkeit um 24 Stunden und ich wollte am Sonntag einkaufen gehen. Hatte mich mehrere Sekunden lang schwer gewundert, wieso der Alimenta-Dealer mit dem besten Mittelwert aus Preis, Entfernung und aktuellem Misstrauen neuerdings Samstags geschlossen hat...

      2. Hallo Achot,

        Ich könnte natürlich ein weiteres Feld einfügen und das Datum intern zusammen zu setzten. Welches Formal wäre denn am besten?

        • 12.03.2017
        • 12-03-2017
        • 2017.03.12
        • 2017-03-12

        MySQL arbeitet intern mit dem untersten Format (YYYY-MM-DD). Aber ein weiteres Feld ist nicht sinnvoll, damit speicherst du die Daten doppelt und läufst Gefahr das nicht das gleiche drin steht - setz die Daten doch gleich beim Import zusammen.

        Oftmals möchte ich z.B. nur nach Jahr suchen oder nach einem Monat oder zeig mir alle Einträge aus 2016 12 an. Da ist so eine Aufteilung schon etwas schönes?

        Nein. Schau dir mal die Datums-und Zeitfunktionen von MySQL an, sowas wie YEAR() und MONTH() existiert - damit lässt sich problemlos nach Einträgen zu einem bestimmten Monat o.ä. suchen.

        Gruß,
        Tobias

        1. Tach!

          • 2017-03-12

          MySQL arbeitet intern mit dem untersten Format (YYYY-MM-DD).

          Genauer gesagt, es präsentiert Datumsdaten in dieser Form und möchte sie in dieser Form übergeben bekommen. Was es intern damit macht, steht auf einem anderen Blatt. Ein DATE benötigt nur 3 Byte Speicherplatz. Da findet also eine Umwandlung in ein internes Format statt.

          Schau dir mal die Datums-und Zeitfunktionen von MySQL an, sowas wie YEAR() und MONTH() existiert - damit lässt sich problemlos nach Einträgen zu einem bestimmten Monat o.ä. suchen.

          Genau das ist der üblicherweise beste Weg: Speicherung im entsprechenden Datentyp und Verwenden der bereitgestellten Funktionen zum Zugreifen darauf oder auf Teile davon.

          dedlfix.

      3. Hallo Achot,

        Oftmals aus einer Excel-Liste und dort ist es üblich das Datum aufzusplitten um danach suchen zu können.

        Mit Verlaub: Das ist Quatsch.

        Bis demnächst
        Matthias

        --
        Rosen sind rot.
    2. Hallo,

      WHERE DATE(haltbar_bis_jahr, haltbar_bis_monat, haltbar_bis_tag) > NOW() + INTERVAL 6 WEEK

      dieses Format stimmt leider nicht. Wenn ich dieses im phpMyAdmin ausführe, erhalte ich folgende Meldung

      #1064 - Fehler in der SQL-Syntax. Bitte die korrekte Syntax im Handbuch nachschlagen bei ' haltbar_bis_monat, haltbar_bis_tag) > NOW() + INTERVAL 6 WEE LIMIT 0, 25' in Zeile 1

      Wo hast du das Beispiel her? Auf https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html finde ich nichts passendes.

      1. Hallo,

        DATE(haltbar_bis_jahr, haltbar_bis_monat, haltbar_bis_tag)

        DATE erwartet ein Datumsobjekt und keine Liste dessen, was du für ein Datum hältst. Vielleicht hilft ein zwischengeschaltetes Cast()?

        Gruß
        Kalk

        1. Keine Ahnung wie ich dieses einsetzten soll. Auf der Seite https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast steht ja nicht gerade viel.

          Außerdem bin ich schon ein Schritt weiter: https://forum.selfhtml.org/self/2017/mar/18/datum-plus-6-wochen/1690408#m1690408 zumindest wird mir kein Fehler mehr angezeigt.

      2. Tach!

        WHERE DATE(haltbar_bis_jahr, haltbar_bis_monat, haltbar_bis_tag) > NOW() + INTERVAL 6 WEEK

        dieses Format stimmt leider nicht. Wenn ich dieses im phpMyAdmin ausführe, erhalte ich folgende Meldung

        #1064 - Fehler in der SQL-Syntax. Bitte die korrekte Syntax im Handbuch nachschlagen bei ' haltbar_bis_monat, haltbar_bis_tag) > NOW() + INTERVAL 6 WEE LIMIT 0, 25' in Zeile 1

        Die Antwort, die dir gegeben wurde, ist also nicht die richtige. Kann ja mal vorkommen.

        Wo hast du das Beispiel her? Auf https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html finde ich nichts passendes.

        Stimmt. Ich dachte erst MAKEDATE() wäre die dazu passende Funktion, aber nein. Ich finde da auch keine Funktion, die aus drei Werten einen Datumswert erzeugt. Da bleibt nur der Umweg, ein Datumsliteral zu erstellen, also einen String im Format 'YYYY-MM-DD'.

        Das kann aber nicht die Lösung sein, denn wenn du auf diese Weise erstmal ein Datum erzeugen musst, um es dann mit den anderen Funktionen auswerten zu können, ist das sehr langsam, weil das für jeden Datensatz extra getan werden muss und kein Index die Suche beschleunigen kann. Das nennt man auch Full-Table-Scan, und sowas will man mit zunehmender Anzahl Datensätze auf jeden Fall vermeiden. Bau lieber deine Tabellenstruktur so um, dass ein echtes Datum gespeichert wird.

        dedlfix.

        1. Hallo,

          Bau lieber deine Tabellenstruktur so um, dass ein echtes Datum gespeichert wird.

          wenn ich also aus meinen drei Feldern

          • haltbar_bis_jahr
          • haltbar_bis_monat
          • haltbar_bis_tag

          ein Feld machen soll, welches Format sollte dieses haben? Date? Und wie soll dann das Datum gespeichert werden? 2017-01-23?

          1. Tach!

            wenn ich also aus meinen drei Feldern

            • haltbar_bis_jahr
            • haltbar_bis_monat
            • haltbar_bis_tag

            ein Feld machen soll, welches Format sollte dieses haben? Date?

            Ja klar, Tag, Monat, Jahr, das ist genau das was DATE spiechert. DATETIME hingegen wäre zu viel, denn den Uhrzeit-Anteil brauchst du ja nicht. TIME fällt gleich ganz aus. Und TIMESTAMP hat andere Aufgaben.

            Und wie soll dann das Datum gespeichert werden? 2017-01-23?

            Das ist als Frage ungenau formuliert, denn wie es gespeichert wird, ist MySQL-Interna. Die richtige Frage ist, wie man das Datum übergeben muss. Und da es so wie es aussieht keine Funktion gibt, die aus den drei Einzelwerten ein DATE erstellen kann, bleibt nur die vorgesehene Literalschreibweise für Datümer.

            Kurze Antwort: ja, diese Form ist richtig: 2017-01-23.

            dedlfix.

            1. Hallo,

              Und wie soll dann das Datum gespeichert werden? 2017-01-23?

              Das ist als Frage ungenau formuliert, denn wie es gespeichert wird, ist MySQL-Interna. Die richtige Frage ist, wie man das Datum übergeben muss. Und da es so wie es aussieht keine Funktion gibt, die aus den drei Einzelwerten ein DATE erstellen kann, bleibt nur die vorgesehene Literalschreibweise für Datümer.

              Kurze Antwort: ja, diese Form ist richtig: 2017-01-23.

              es geht auch date(concat(jahr,'-',monat,'-',tag))

              Da dürfen die drei Bestandteile dann auch verkürzt sein, also dass nach dem Concat() zB. nur '2017-3-19' heraus kommt.

      3. Schaue in der MySQL-Anleitung, ob es dort einen date()-Befehl gibt, der Einzelteile eines Datums annimmt.

        WHERE DATE(haltbar_bis_jahr, haltbar_bis_monat, haltbar_bis_tag) > NOW() + INTERVAL 6 WEEK

        dieses Format stimmt leider nicht.

        Und warum wohl habe ich extra noch drüber geschrieben, du mögest bitte in der Anleitung nachschauen, ob es dort einen passenden Befehl gibt?

        Wo hast du das Beispiel her?

        Das habe ich mir aus den Fingern gesaugt, weil ich keine Lust hatte, für dich in deiner MySQL-Anleitung nachzugucken.

        Auf https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html finde ich nichts passendes.

        Dann solltest du genauer hingucken: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_str-to-date wäre zum Beispiel etwas. Aufrufundguckdauer: Kaum eine halbe Minute.

  2. Ok, so stimmt wohl die Abfrage. Zumindest meldet phpMyAdmin keinen Fehler mehr

    SELECT id, titel FROM artikel 
      WHERE DATE('haltbar_bis_jahr-haltbar_bis_monat-haltbar_bis_tag') > NOW() + INTERVAL 2 WEEK
    

    komisch ist nur, es wird kein Artikel gefunden obwohl ich einen Test-Artikel mit dem Datum 2017-04-01 eingetragen habe.

    1. Finde etwas WHERE 2017-04-01 > "jetzt plus 2 Wochen".
      In zwei Wochen ist der 2. April, das ist größer als der 1.

      1. Hallo,

        Das Statement ist falsch.
        Einen String mit einem Datum zu vergleichen ist wenig sinnvoll!

        da muss mindestens ein concat(haltbar_bis_jahr,'-',haltbar_bis_monat,'-',haltbar_bis_tag) her. Und dann müsste man sich vermutlich noch um die führenden Nullen kümmern, damit nicht nachher 2017-4-1 dort steht. Ich weiß nicht, ob das trotzdem als gültige Datumsrepräsentation erkannt werden würde.

        Liebe Grüße RR

        1. Hallo,

          Ich weiß nicht, ob das trotzdem als gültige Datumsrepräsentation erkannt werden würde.

          M.E. hilft da der Cast.

          Gruß
          Kalk

          1. Hallo,

            Ich weiß nicht, ob das trotzdem als gültige Datumsrepräsentation erkannt werden würde.

            M.E. hilft da der Cast.

            Ich habe es eben ausprobiert. Wenn man ein date() drum herum schreibt um den zusammengesetzten String, reichen auch die verkürzten Bestandteile ohne die führenden Nullen. Wenn man es aber selber als String übergibt, muss das Format genau passen, sonst kommt nur '0000-00-00 00:00:00' dabei heraus.

            1. Hallo,

              so funktioniert es wohl

              SELECT id, titel FROM artikel WHERE date(concat(haltbar_bis_jahr,'-',haltbar_bis_monat,'-',haltbar_bis_tag)) <= NOW() + INTERVAL 6 WEEK
              

              Das heißt doch, such mir ALLES, was kleiner / gleich jetzt + 6 Wochen ist, richtig? Deshalb findet er auch diesen Eintrag: 01. 04. 2017

              EDIT: Geht wohl doch nicht richtig, denn der Eintrag 31.02.2017 wird nicht gefunden 😕

              1. Hi,

                EDIT: Geht wohl doch nicht richtig, denn der Eintrag 31.02.2017 wird nicht gefunden 😕

                wie auch - dieses Datum gibt es ja auch gar nicht. Zumindest nicht auf diesem Planeten.

                cu,
                Andreas a/k/a MudGuard

                1. So eine scheiße passiert, wenn man von Hand in der Datenbank fummelt und nicht auf den Monat achtet 😕

                  Danke dir!

                  Meine funktion sieht nun so aus

                  function mhd($mysqli) {
                          $stmt = $mysqli->prepare("SELECT id, titel, haltbar_bis_jahr, haltbar_bis_monat, 
                                                           haltbar_bis_tag
                                                    FROM artikel WHERE date(concat(haltbar_bis_jahr,'-
                                              ',haltbar_bis_monat,'-',haltbar_bis_tag)) < NOW() + INTERVAL 6 WEEK");
                          $stmt->execute();
                          $stmt->bind_result($id, $titel, $haltbar_bis_jahr, $haltbar_bis_monat, $haltbar_bis_tag);
                          $stmt->store_result();
                          
                          if($stmt->num_rows() >  0) {     
                              
                              while ($stmt->fetch()){
                              
                                  $mhd[] = array( 
                                      'id'                  => $id,
                                      'titel'               => $titel,
                                      'haltbar_bis_jahr'    => $haltbar_bis_jahr,
                                      'haltbar_bis_monat'   => $haltbar_bis_monat,
                                      'haltbar_bis_tag'     => $haltbar_bis_tag
                                  );
                              }
                              return $mhd;
                              }
                      }
                  

                  Jetzt findet er folgende Artikel:

                  • 04.04.2017
                  • 01.04.2017
                  • 28.02.2017

                  Den

                  • 31.12.2017
                  • 12.07.2018

                  finde er nicht. Damit sollte alles richtig laufen, oder? Danke für die Hilfe.

        2. Hallo,

          wenn ich es so machen, findet er alle Einträge

          SELECT id, titel FROM artikel WHERE concat(haltbar_bis_jahr,'-',haltbar_bis_monat,'-',haltbar_bis_tag) <= NOW() + INTERVAL 6 WEEK
          
      2. Das heißt der Fehler liegt hier

        
        > NOW() + INTERVAL 2 WEEK
        
        

        Ich muss mit dem NOW() etwas ändern? Er soll mir ja ALLE anzeigen die +2 Week sind, auch die, die in Vergangenheit liegen.

        1. Nehme an du hast es inzwischen richtig?
          Falls nicht, ich meinte wenn du alles "früher als" haben willst, musst du < nehmen, nicht >
          Oder anders rum, wenn du > nimmst, ist es ok wenn dein Testeintrag 1. April nicht erscheint.