Kalli: mysql: 2 alternative WHERE Klauseln anwenden

Hallo,

ich habe eine Abfrage, die mir die Datensätze anzeigen soll, die ein bestimmtes Datum überschreiten. Es gibt aber hierfür 2 relevante Spalten (aus verschiedenen Tabellen, weil normalisiert).

Spalte-Datum1 ---- Spalte-Tage1  ----  Spalte-Datum2 ---- Spalte-Tage2

Nun sollen die Datensätze ermittelt werden, bei denen (Datum2+Tage2) vom heutigen Datum überschritten wird, bzw. bei NULL-Wert (weil der Eintrag Datum2 sowie Tage2 nicht vorhanden ist) sollen stattdessen die Tage1 zum Datum1 genommen werden.

Jede WHERE Klausel selber würde ich schon schaffen, aber geht das auch so?

<prosa>
Ermittle Datensätze
WHERE
ADDDATE(m2.datum, INTERVAL m2.Tage DAY) > heute
und falls das NULL ergibt, dann eben
ADDDATE(m1.datum, INTERVAL m1.Tage DAY) > heute
</prosa>

Kalli

  1. Tach!

    Jede WHERE Klausel selber würde ich schon schaffen, aber geht das auch so?
    <prosa>
    Ermittle Datensätze WHERE
    ADDDATE(m2.datum, INTERVAL m2.Tage DAY) > heute
    und falls das NULL ergibt, dann eben
    ADDDATE(m1.datum, INTERVAL m1.Tage DAY) > heute
    </prosa>

    ADDDATE() hat eine zweite Form: ADDDATE(datum, tage), damit sparst du die Schlüsselwörter INTERVAL und DAY. Ansonsten gäbe es auch noch die Form: datum + INTERVAL tage DAY (siehe Beschreibung zu DATE_ADD()). Aber zu deiner eigentlichen Frage: IFNULL() oder auch das allgemeine IF() lässt sich da verwenden.

    dedlfix.

    1. Tach,

      Jede WHERE Klausel selber würde ich schon schaffen, aber geht das auch so?
      <prosa>
      Ermittle Datensätze WHERE
      ADDDATE(m2.datum, INTERVAL m2.Tage DAY) > heute
      und falls das NULL ergibt, dann eben
      ADDDATE(m1.datum, INTERVAL m1.Tage DAY) > heute
      </prosa>

      Aber zu deiner eigentlichen Frage: IFNULL() oder auch das allgemeine IF() lässt sich da verwenden.

      sollte nicht auch ein OR reichen?

      WHERE (m2.datum ISNULL AND ADDDATE(m1.datum, tage)) OR ADDDATE(m2.datum, tage)

      mfg
      Woodfighter

      1. sollte nicht auch ein OR reichen?

        WHERE (m2.datum ISNULL AND ADDDATE(m1.datum, tage)) OR ADDDATE(m2.datum, tage)

        Hallo Jens.

        Hört sich für mich ebenfalls schlüssig an.

        Welches ist (so beide korrekt sind) performanter?

        Kalli

        1. Tach,

          Welches ist (so beide korrekt sind) performanter?

          mit IFNULL werden vermutlich (je nachdem, wie ADDDATE implementiert ist) m2.datum und m2.tage ausgelesen und an die Funktion übergeben, bevor feststeht, dass das Ergebnis NULL ist, meine Variante braucht nur m2.datum; aber ehrlichgesagt würde es mich wundern, wenn das meßbar wird bevor man sehr viele Datensätze damit bearbeitet.

          mfg
          Woodfighter

      2. Tach!

        sollte nicht auch ein OR reichen?
        WHERE (m2.datum ISNULL AND ADDDATE(m1.datum, tage)) OR ADDDATE(m2.datum, tage)

        Sieht auch passend aus, wenn da noch ein Leerzeichen zwischen das IS NULL und der Vergleich mit dem heutigen Datum hinzukommt.

        dedlfix.

        1. Tach,

          sollte nicht auch ein OR reichen?
          WHERE (m2.datum ISNULL AND ADDDATE(m1.datum, tage)) OR ADDDATE(m2.datum, tage)

          Sieht auch passend aus, wenn da noch ein Leerzeichen zwischen das IS NULL und der Vergleich mit dem heutigen Datum hinzukommt.

          natürlich: WHERE (m2.datum IS NULL AND ADDDATE(m1.datum, tage) > heute) OR ADDDATE(m2.datum, tage) > heute

          mfg
          Woodfighter

    2. Hallo dedlfix.

      danke für Deine Hilfe.

      Ich habe es nun so umgesetzt

        
      WHERE IFNULL(ADDDATE(m2.datum, INTERVAL m.tage2 DAY) < now(),ADDDATE(m1.datum , INTERVAL m1.tage1 DAY) < now())  
      
      

      und es scheint genau das zu machen, was ich erreichen wollte.

      Verstehe ich Dich richtig, daß

        
      WHERE IFNULL(ADDDATE(m2.datum, m.tage2) < now(),ADDDATE(m1.datum , m1.tage1) < now())  
      
      

      ebenfalls korrekt gewesen wäre?

      Kalli

      1. Tach!

        Ich habe es nun so umgesetzt

        WHERE IFNULL(ADDDATE(m2.datum, INTERVAL m.tage2 DAY) < now(),ADDDATE(m1.datum , INTERVAL m1.tage1 DAY) < now())

        
        > und es scheint genau das zu machen, was ich erreichen wollte.  
          
        Wann immer NULL in einem Ausdruck auftaucht, ist das Ergebnis NULL (Ausnahmen sind die speziellen NULL-Operatoren à la IS NULL und einige Funktionen). Somit wird der erste Teilausdruck zu NULL, wenn m2.datum oder m.tage2 NULL sind. Und dann wird halt der m1-Ausdruck verwendet. Soweit scheint dein Scheinen auch mit der Logik übereinzustimmen.  
          
        
        > Verstehe ich Dich richtig, daß  
        > ~~~sql
          
        
        > WHERE IFNULL(ADDDATE(m2.datum, m.tage2) < now(),ADDDATE(m1.datum , m1.tage1) < now())  
        > 
        
        

        ebenfalls korrekt gewesen wäre?

        Verifiziere meine (und anderer) Aussagen am besten mit der offiziellen Dokumentation.

        dedlfix.

        1. Tach!

          Ich habe es nun so umgesetzt

          WHERE IFNULL(ADDDATE(m2.datum, INTERVAL m.tage2 DAY) < now(),ADDDATE(m1.datum , INTERVAL m1.tage1 DAY) < now())

          
          > > und es scheint genau das zu machen, was ich erreichen wollte.  
          >   
            
            
          Was ist, wenn in der Tabelle 2, also dem ersten Teil meiner IFNULL-Frage mehr als 1 Eintrag steht und ich hier den neueren Eintrag samt seiner "Tage-Spalte" brauche? Neuer meint, daß z.b. das Datum jünger ist oder auch die ID des Eintrages höher ist.  
            
          Kann man das noch mit hier herein bringen oder muß ich dieses Problem dann ganz anders angehen?  
            
          Kalli
          
          1. Om nah hoo pez nyeetz, Kalli!

            Dein primäres Ziel sollte nicht die Vermeidung eines Doppelpostings und des damit verbundenen Rüffels, sondern das Zusammenhalten aller relevanten Informationen im Interesse einer effizienten Hilfeleistung sein.

            Matthias

            --
            Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Müll und Müllerstochter.

            1. Dein primäres Ziel sollte nicht die Vermeidung eines Doppelpostings und des damit verbundenen Rüffels

              Woher willst Du mein primäres Ziel kennen?

              Kalli

              1. Tach!

                Dein primäres Ziel sollte nicht die Vermeidung eines Doppelpostings und des damit verbundenen Rüffels
                Woher willst Du mein primäres Ziel kennen?

                Das ist bei Fragenden immer, die bestmögliche Antwort zu bekommen. Und da hilft es sehr, wenn man es den Antwortenden so einfach wie möglich macht, indem man zum Beispiel alle Informationen in einem Thread behält und nicht quer übers Forum verteilt. Diese Regel der Doppelposting-Vermeidung ist also nicht zum Gängeln da, sondern im eigenen Interesse des Probleminhabers.

                dedlfix.

                1. Das ist bei Fragenden immer, die bestmögliche Antwort zu bekommen. Und da hilft es sehr, wenn man es den Antwortenden so einfach wie möglich macht, indem man zum Beispiel alle Informationen in einem Thread behält und nicht quer übers Forum verteilt. Diese Regel der Doppelposting-Vermeidung ist also nicht zum Gängeln da, sondern im eigenen Interesse des Probleminhabers.

                  Hi dedlfix,

                  ich finde die Doppelposting-Regelung absolut ok. Vielleicht wären 2 Checkboxen für Fragende ganz praktisch:

                  1. Problem gelöst
                  2. Habe doch noch ne Nachfrage

                  Ich war mir einfach nicht ganz sicher, wie auffällig eine weitere Nachfrage von mir noch sein würde, deshalb habe ich mir eine auffällige Überschrift überlegt.

                  Kalli

                  1. Om nah hoo pez nyeetz, Kalli!

                    Ich war mir einfach nicht ganz sicher, wie auffällig eine weitere Nachfrage von mir noch sein würde, deshalb habe ich mir eine auffällige Überschrift überlegt.

                    angemeldete Nutzer können gelesene von ungelesenen Nachrichten unterscheiden. Eine Anmeldung tut nicht weh und erlaubt Einstellungen am Forum nach deinem Geschmack vorzunehmen.

                    Matthias

                    --
                    Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Ball und Ballast.

              2. Om nah hoo pez nyeetz, Kalli!

                Woher willst Du mein primäres Ziel kennen?

                naja, da du eine Frage stellst, wird es wohl eine Antwort sein.

                Matthias

                --
                Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Fass und Fassade.

                1. Om nah hoo pez nyeetz, Kalli!

                  Woher willst Du mein primäres Ziel kennen?
                  naja, da du eine Frage stellst, wird es wohl eine Antwort sein.

                  Matthias

                  Hallo Mathias.

                  Das ist nur die halbe Wahrheit. Tatsächlich hatte ich nie vor, ein Doppelposting zu schreiben, weiß aber, daß alleine dieses Wort hier im Forum Aufmerksamkeit erzeugt. :-P

                  Ich weiß, daß das bei angemeldeten Stammusern nicht zwingend nötig ist. Da das Thema aber eigentlich als gelöst galt, fand ich das angebracht.

                  Und es hat ja sogar Dich in diesen Thread gelockt, hat also geklappt ;-)

                  Kalli

          2. Tach!

            Was ist, wenn in der Tabelle 2, also dem ersten Teil meiner IFNULL-Frage mehr als 1 Eintrag steht und ich hier den neueren Eintrag samt seiner "Tage-Spalte" brauche? Neuer meint, daß z.b. das Datum jünger ist oder auch die ID des Eintrages höher ist.

            Die ID ist normalerweise kein Sortierkriterium, weil die nicht garantiert aufsteigend vergeben wird. Die solltest du üblicherweise aus solchen Neuer-Älter-Betrachtungen herauslassen.

            Kann man das noch mit hier herein bringen oder muß ich dieses Problem dann ganz anders angehen?

            Das ist der zweite Schritt. Der erst wäre, zunächst einmal eine Query zu formulieren, die die gewünschten Datensätze selektiert. Dass diese dann in die andere zu integrieren ist, muss man lediglich im Hinterkopf behalten, damit man keine unpassenden Lösungen im ersten Schritt erzeugt.

            Das Problem jedenfalls ist, dass du mehr als einen Wert benötigst. Ein einzelner ließe sich sehr gut in einer correlated subquery ermitteln. Mehrere müssen in einer Ergebnismenge gesammelt und gejoint werden. Und hier wiederum ist es nicht so einfach möglich, Gruppen zu bilden und aus dieser Gruppe Werte aus einen Datensatz zu bekommen und nicht nur ein Aggregat-Ergebnis (Summe, Maximalwert, ...).

            Du willst also den neuesten Datensatz haben - okay, Maximalwert ermitteln. Damit hast du aber noch nicht den Datensatz (oder in deinem Fall das Tagesfeld), sondern nur ebendiesen Maximalwert. Und der kann auch in mehreren Datensätzen der Gruppe vorkommen. LIMIT gibt es nicht innerhalb einer Gruppierung und wenn du auf das Tagesfeld zugreifst, geht das erstens nur unter MySQL (weil das Tagesfeld nicht Bestandteil der Gruppierung und auch nicht aggregiert wurde - was nur MySQL zulässt, anderswo aber als Fehler angesehen wird) und zweitens gibt MySQL nur einen beliebigen Wert aus der Gruppe und nicht unbedingt einen zum Maximalwert-Datensatz gehörigen Wert aus. Dieser Weg ist also üblicherweise unbrauchbar.

            Vielleicht gibt es einen Trick 17, der mir grad nicht geläufig ist (vielleicht irgendwas mit Self-Join). Das waren jedenfalls viele Wörter, nur um zu sagen wie es nicht geht, was dich auch nicht näher zur Lösung bringt, aber hier muss ich erstmal passen.

            dedlfix.

            1. Hallo dedlfix,

              Die ID ist normalerweise kein Sortierkriterium, weil die nicht garantiert aufsteigend vergeben wird. Die solltest du üblicherweise aus solchen Neuer-Älter-Betrachtungen herauslassen.

              Auch als autoincremment-Wert nicht?

              Das ist der zweite Schritt. Der erst wäre, zunächst einmal eine Query zu formulieren, die die gewünschten Datensätze selektiert. Dass diese dann in die andere zu integrieren ist, muss man lediglich im Hinterkopf behalten, damit man keine unpassenden Lösungen im ersten Schritt erzeugt.

              Ok.

              Das Problem jedenfalls ist, dass du mehr als einen Wert benötigst. Ein einzelner ließe sich sehr gut in einer correlated subquery ermitteln. Mehrere müssen in einer Ergebnismenge gesammelt und gejoint werden. Und hier wiederum ist es nicht so einfach möglich, Gruppen zu bilden und aus dieser Gruppe Werte aus einen Datensatz zu bekommen und nicht nur ein Aggregat-Ergebnis (Summe, Maximalwert, ...).

              Genau daran knabbere ich seit graumer Zeit, daher die "doppelte" Nachfrage.

              Du willst also den neuesten Datensatz haben - okay, Maximalwert ermitteln. Damit hast du aber noch nicht den Datensatz

              Ganz genau so ist es.

              geht das erstens nur unter MySQL (weil das Tagesfeld nicht Bestandteil der Gruppierung und auch nicht aggregiert wurde - was nur MySQL zulässt,

              Hatte ich auch schon erroiert, damit hätte ich leben können, weil ich mysql verwende.

              anderswo aber als Fehler angesehen wird) und zweitens gibt MySQL nur einen beliebigen Wert aus der Gruppe und nicht unbedingt einen zum Maximalwert-Datensatz gehörigen Wert aus. Dieser Weg ist also üblicherweise unbrauchbar.

              Und ebendies hatte ich befürchtet. Und dachte, es gäbe hier eine brauchbare Lösung für.

              Vielleicht gibt es einen Trick 17, der mir grad nicht geläufig ist (vielleicht irgendwas mit Self-Join). Das waren jedenfalls viele Wörter, nur um zu sagen wie es nicht geht, was dich auch nicht näher zur Lösung bringt, aber hier muss ich erstmal passen.

              Doch, das bringt mich ganz genau zur Lösung.
              Denn dann werde ich die DB ein klein wenig umstrukturieren. Ich weiß noch nicht ganz genau, wie ich das mache, aber es gäbe einige Möglichkeiten. Ich könnte z.b. anstelle eines INSERTS ein Update nutzen. Damit hätte ich augenblicklich keine "doppelten" Datensätze mehr. Nachteil wäre, daß ich die Historie des Datensatzes verlieren würde. Aber das könnte ich anders regeln, zb. über 2 Tabellen. 1 Tabelle für den aktuellen Stand, die 2. Tabelle für die "historischen" Stände.

              Das würde beim Eintragen etwas mehr Arbeit bedeuten, aber das Auslesen der Datensätze würde erheblich vereinfacht. Und da ich bei diesem Programmteil noch in den Anfängen stecke, ist es problemlos möglich, noch an der DB-Strucktur zu feilen.

              Also doch geholfen, danke :-)

              Kalli

              1. Tach!

                Die ID ist normalerweise kein Sortierkriterium, weil die nicht garantiert aufsteigend vergeben wird. Die solltest du üblicherweise aus solchen Neuer-Älter-Betrachtungen herauslassen.
                Auch als autoincremment-Wert nicht?

                Nun, Theorie und Praxis. Theoretisch kann der Wert überlaufen und dann fängt er wieder unten an. Theoretisch dürftest du auch keinerlei Garantie finden, dass auto_increment immer aufsteigend vergibt. Praktisch mag das zutreffen. Praktisch sind auch Regelübertretungen (wenn man das obige mal als Regel ansieht) problemlos möglich, solange man mit den Konsequenzen leben kann oder sie als gering einschätzt. Ich selbst versucht, die ID-Verwendung für solche Zwecke zu vermeiden, aber bevor ich in Schönheit sterbe, muss manchmal doch die ID als (zusätzliches) Sortierkriterium herhalten.

                Denn dann werde ich die DB ein klein wenig umstrukturieren. Ich weiß noch nicht ganz genau, wie ich das mache, aber es gäbe einige Möglichkeiten. Ich könnte z.b. anstelle eines INSERTS ein Update nutzen. Damit hätte ich augenblicklich keine "doppelten" Datensätze mehr.

                INSERT ... ON DUPLICATE KEY UPDATE ... wäre da vielleicht ein Kandidat.

                Nachteil wäre, daß ich die Historie des Datensatzes verlieren würde. Aber das könnte ich anders regeln, zb. über 2 Tabellen. 1 Tabelle für den aktuellen Stand, die 2. Tabelle für die "historischen" Stände.

                Oder ein Flag aktuell/veraltet. Das würde dann aber zwei Abfragen beim Hinzufügen bedeuten. Erstmal alle Datensätze desselben Kriteriums auf veraltet setzen, dann den neuen einfügen. Und damit bist du dann bei Transaktionen angekommen, wenn dieser Vorgang auch bei Fehlern nicht nur teilausgeführt zurückbleiben soll. Eine Transaktion braucht es aber auch für die Wegspeichern-Methode, wenn sie teilausführungsunanfällig sein soll.

                Das würde beim Eintragen etwas mehr Arbeit bedeuten, aber das Auslesen der Datensätze würde erheblich vereinfacht. Und da ich bei diesem Programmteil noch in den Anfängen stecke, ist es problemlos möglich, noch an der DB-Strucktur zu feilen.

                Wenn das Schreiben sehr schnell gehen muss (zum Beispiel bei hoher Ereignisanzahl pro Zeiteinheit), dann wäre eher der Schreibvorgang optimierwürdig. Das Auslesen kann man dann auch mit einem mehrschrittigen Programm statt mit einer mit Bauch- und Kopfschmerzen zusammengeschraubten Einzelquery erledigen. Auch eine Stored Procedure wäre dazu geeignet. Aber wenn sich die Eintragungen in Grenzen halten, dann doch lieber die Duplikatsvermeidungsmethoden.

                dedlfix.