Nik: Doppelte Einträge trotz distinct

Hi,

verständlicherweise werden mir "doppelte" Einträge angezeigt, wenn ich nachfolgende Query stelle uund ich 2 verschiedene mahnstufen habe:

  
select DISTINCT  
         r.Rechnungsnummer,  
		 ADDDATE(r.R_Datum , INTERVAL r.Faelligkeit DAY),  
         m.Mahnstatus,  
		 ADDDATE(m.M_Datum, INTERVAL m.Faelligkeit DAY)  
         from rechnungen r  
		 LEFT JOIN mahnungen m  
		 ON r.RechnungenID = m.RechnungenID  
         WHERE  
         r.B_art="RG"  

Es werden solange keine doppelten Rechnungsnummern angezeigt, solange max. ein Mahnstatus existiert.

Sobald aber für eine Rechnung eine ZA (Zahlungserinnerung) und eine MAH (Mahnung) existiert, wird diese Rechnung (verständlicherweise) 2 mal als Rechungsnummer aufgeführt.

Wie kann ich das verhinden? Ich bräuchte sie nur 1 mal.

Nik

  1. Moin,

    Wie kann ich das verhinden? Ich bräuchte sie nur 1 mal.

    1. Du findest ein vorhandenes, eindeutiges Merkmal für den JOIN, was selbiges verhindert
       (beispielsweise den Mahnstatus) und prüfst darauf.
    2. Du kreierst ein Merkmal für den JOIN, mit dem du selbiges verhindern kannst (beispielsweise
       einen Timestamp, der dich den neuesten Status abfragen lässt) und prüfst darauf.
    3. Du selektierst den Mahnstatus nicht. Er scheint ja nicht wichtig zu sein.

    Grüße Marco

    --
    Ich spreche Spaghetticode - fließend.
      1. Du findest ein vorhandenes, eindeutiges Merkmal für den JOIN, was selbiges verhindert
           (beispielsweise den Mahnstatus) und prüfst darauf.

      Ah klar. Brett vorm Kopf gehabt, danke.

      Nik

        1. Du findest ein vorhandenes, eindeutiges Merkmal für den JOIN, was selbiges verhindert
             (beispielsweise den Mahnstatus) und prüfst darauf.

        Hm, immer noch Brett vorm Kopf?

        Ich dachte

          
        select DISTINCT  
                 r.Rechnungsnummer,  
                         ADDDATE(r.R_Datum , INTERVAL r.Faelligkeit DAY),  
                 m.Mahnstatus,  
                         ADDDATE(m.M_Datum, INTERVAL m.Faelligkeit DAY)  
                 from rechnungen r  
                         LEFT JOIN mahnungen m  
                         ON r.RechnungenID = m.RechnungenID  
                 WHERE  
                 r.B_art="RG"  
          
        GROUP BY r.RechnungsNummer HAVING max(m.ID)  
          
        
        

        würde das Problem lösen, aber dann  wird mir anscheinend nur die Rechnung mit der höchsten m.ID angezeigt.

        Ich dachte, es würden so alle Rechnungsnummern je 1 mal angezeigt und falls 2 (oder mehr) Mahnstati vorhanden wären, würde der mit der höchsten ID genommen?

        Nik

        1. Moin,

          würde das Problem lösen, aber dann  wird mir anscheinend nur die Rechnung mit der höchsten m.ID angezeigt.
          Ich dachte, es würden so alle Rechnungsnummern je 1 mal angezeigt und falls 2 (oder mehr) Mahnstati vorhanden wären, würde der mit der höchsten ID genommen?

          Ich bin kein SQL-Experte, aber die Methode mit dem HAVING ist meines Erachtens hier nicht zielführend. Ich würde das vielleicht folgendermaßen machen, mit der Gewissheit, dass es irgendwie noch besser geht:

          SELECT DISTINCT  
             r.Rechnungsnummer,  
             ADDDATE(r.R_Datum , INTERVAL r.Faelligkeit DAY),  
             m.Mahnstatus,  
             ADDDATE(m.M_Datum, INTERVAL m.Faelligkeit DAY)  
          FROM rechnungen r  
          LEFT JOIN mahnungen m  
             ON r.RechnungenID = m.RechnungenID  
             AND m.Id = (SELECT MAX(tmp.Id) from mahnungen tmp WHERE tmp.RechnungenID = r.RechnungenID)  
          WHERE  
             r.B_art="RG"
          

          Damit joinst du immer nur den Datensatz mit der größten ID aus Mahnungen, welche mit der Rechnungs-ID übereinstimmt, an das Ergebnis.

          Wie gesagt: Das ist eine Lösung, die wahrscheinlich unperformant und verbesserungswürdig ist. Vielleicht meldet sich noch ein SQL-Experte. Oder zumindest ein Amateur. Oder wenigstens ein Semi-Amateur ;)

          Grüße Marco

          --
          Ich spreche Spaghetticode - fließend.
          1. Tach!

            Damit joinst du immer nur den Datensatz mit der größten ID aus Mahnungen, welche mit der Rechnungs-ID übereinstimmt, an das Ergebnis.
            Wie gesagt: Das ist eine Lösung, die wahrscheinlich unperformant und verbesserungswürdig ist.

            Zumindest ist eine ID oftmals kein geeignetes Sortierkriterium, weil niemand garantiert, dass diese aufsteigend vergeben werden. Ein Datumswert (solange er fein genug gespeichert wird) ist da üblicherweise besser.

            dedlfix.

        2. Tach!

          select DISTINCT

          r.Rechnungsnummer,
                           ADDDATE(r.R_Datum , INTERVAL r.Faelligkeit DAY),
                   m.Mahnstatus,
                           ADDDATE(m.M_Datum, INTERVAL m.Faelligkeit DAY)
                   from rechnungen r
                           LEFT JOIN mahnungen m
                           ON r.RechnungenID = m.RechnungenID
                   WHERE
                   r.B_art="RG"

          GROUP BY r.RechnungsNummer HAVING max(m.ID)

          
          > würde das Problem lösen, aber dann  wird mir anscheinend nur die Rechnung mit der höchsten m.ID angezeigt.  
            
          Mit solch einer Konstruktion holst du dir unter umständen mehr Probleme an Land. Nur MySQL erlaubt, andere Felder als die im GROUP BY angegeben, zuzüglich Aggregatfunktionen (MAX(), MIN(), SUM(), etc.) zu selektieren. Das Ergebnis ist dann jedenfalls undefiniert. Es wird ein beliebiger Wert aus der Gruppe genommen. Solch eine Gruppe produziert ja immer nur eine Ergebniszeile pro Gruppen-Wert. Man kann solceh Abfragen verwenden, wenn man genau weiß, dass die Datei innerhelb der Gruppe eindeutig sind, was vor allem bei redundanten Daten der Fall ist, die man ja aber eigentlich durch Normalisierung wegoptimiert.  
            
          Das HAVING filtert auch nicht Gruppenwerte sondern kommt erst zur Anwendung, wenn die Ergebnismenge berechnet ist.  
            
          
          > Ich dachte, es würden so alle Rechnungsnummern je 1 mal angezeigt und falls 2 (oder mehr) Mahnstati vorhanden wären, würde der mit der höchsten ID genommen?  
            
          Die richtige Mehrzeahl von Status ist übrigens [Status](http://www.duden.de/rechtschreibung/Status) (gesprochen mit etwas langgezogenerem u).  
            
          Formulier doch erstmal, was überhaupt das Ergebnis werden soll. misterunknown hat ja schon gemutmaßt, dass der Mahnstatus irrelevant ist, weil du, egal wieviele davon existieren, nur eine Rechnung sehen willst. Du fragst ihn trotzdem ab, also muss er eine Bedeutung haben, beziehungsweise musst du eine Regel haben, was bei mehrfachen Mahnstatus passieren soll.  
            
            
          dedlfix.
          
          1. Formulier doch erstmal, was überhaupt das Ergebnis werden soll. misterunknown hat ja schon gemutmaßt, dass der Mahnstatus irrelevant ist, weil du, egal wieviele davon existieren, nur eine Rechnung sehen willst. Du fragst ihn trotzdem ab, also muss er eine Bedeutung haben, beziehungsweise musst du eine Regel haben, was bei mehrfachen Mahnstatus passieren soll.

            Hi dedlfix, Hi misterunknown,

            Korrekt. Sehe will ich nur 1 Rechnungsnummer, aber die Info, auf welchem Mahnstatus sie gerade steht, benötige ich ebenfalls. Daraus generiere ich einen Link, der beim Anklicken ein Emailformular generiert, das den nächst höheren Mahnstatus einleitet.

            Gruß, Nik

            1. Tach!

              Sehe will ich nur 1 Rechnungsnummer, aber die Info, auf welchem Mahnstatus sie gerade steht, benötige ich ebenfalls. Daraus generiere ich einen Link, der beim Anklicken ein Emailformular generiert, das den nächst höheren Mahnstatus einleitet.

              Ich finde grad keine einfache Lösung dafür. Das ist etwas komplexer als gedacht. Du brauchst aus der Mahnungstabelle zu jeder Rechnungsnummer den neuesten Datensatz. Da hören schon meine Ideen auf. GROUP BY ist nicht verwendbar, wenn man es ordentlich machen will. Wenn nicht, ist es unsicher, weil zwar das MAX(Datum) aus der Gruppe, aber nicht der zugehörige Status ermittelt werden kann.

              Am einfachsten sind korrelierte Subselects, aber da du zwei Felder benötigst, sind die weniger gut verwendbar.

              SELECT  
                r.*,  
                (SELECT status FROM m WHERE RechnungenID = r.RechnungenID ORDER BY date DESC LIMIT 1),  
                (SELECT date FROM m WHERE RechnungenID = r.RechnungenID ORDER BY date DESC LIMIT 1)  
              FROM r
              

              Zumindest bringt das das gewünschte Ergebnis, ist aber umständlich. Die Subquery lässt sich in der Form auch nicht ins FROM verlegen, dann könnte sie zwar mehr als ein Feld selektieren, sind aber nicht korrelierbar. Die Auswertung der SELECT-Klausel der Hauptquery ist zu dem Zeitpunkt noch nicht an der Reihe. Das Herauspicken des einen gewünschten m-Datensatzes zu einer RechnungenID geht hier nur über GROUP BY, mit den bereits genannten Unwägbarkeiten. - Ich muss hier erstmal passen.

              dedlfix.