Ankündigung

Einklappen
Keine Ankündigung bisher.

LEFT JOIN mit aktuellstem Wert, auch NULL

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • LEFT JOIN mit aktuellstem Wert, auch NULL

    Hi,

    klingt nach einem ganz banales Standardproblem: Es gibt Benutzer, jeder Benutzer hat keine, eine, oder mehrere Adressen (historisch). Aufgeteilt und normalisiert in zwei Tabellen mit folgender Definition:

    Code:
    CREATE TABLE `Benutzer` (
    `Benutzer_ID` INT NOT NULL PRIMARY KEY ,
    `Name` VARCHAR( 250 ) NOT NULL 
    ) ENGINE = MYISAM ;
    
    CREATE TABLE `Adresse` (
    `Adresse_ID` INT NOT NULL PRIMARY KEY ,
    `Benutzer_ID` INT NOT NULL ,
    `Strasse` VARCHAR( 250 ) NOT NULL 
    ) ENGINE = MYISAM ;
    
    
    
    INSERT INTO `benutzer` (
    `benutzer_ID` ,`Name` 
    )
    VALUES
    (1 , 'Han Solo'),
    (2 , 'Chewbacca'),
    (3 , 'Yoda');
    
    INSERT INTO `adresse` (
    `Adresse_ID` ,`Benutzer_ID` ,`Strasse` 
    )
    VALUES
    (1 , '1', 'Corellia'),
    (2 , '1', 'Ylesia'),
    (3 , '1', 'Todesstern'),
    (4 , '2', 'Kashyyyk');
    Ich suche nach allen Benutzern

    Code:
    SELECT
    	B.Benutzer_ID, B.Name
    FROM Benutzer B
    
    1	Han Solo
    2	Chewbacca
    3	Yoda
    Ich will auf jeden Fall alle Benutzer haben, und wenn vorhanden auch noch eine Adresse

    Code:
    SELECT
    	B.Benutzer_ID, B.Name, A.Adresse_ID, A.Strasse
    FROM Benutzer B
    LEFT JOIN Adresse A ON A.Benutzer_ID = B.Benutzer_ID
    
    1	Han Solo	1	Corellia
    1	Han Solo	2	Ylesia
    1	Han Solo	3	Todesstern
    2	Chewbacca	4	Kashyyyk
    3	Yoda	NULL	NULL
    Ich brauche aber immer nur die letzte Adresse

    Code:
    SELECT
    	B.Benutzer_ID, B.Name, A.Adresse_ID, A.Strasse
    FROM Benutzer B
    LEFT JOIN Adresse A ON A.Benutzer_ID = B.Benutzer_ID
    GROUP BY B.Benutzer_ID
    
    1	Han Solo	1	Corellia
    2	Chewbacca	4	Kashyyyk
    3	Yoda	NULL	NULL
    das war nix. Also nach Adresse_ID sortieren

    Code:
    SELECT
    	B.Benutzer_ID, B.Name, A.Adresse_ID, A.Strasse
    FROM Benutzer B
    LEFT JOIN Adresse A ON A.Benutzer_ID = B.Benutzer_ID
    GROUP BY B.Benutzer_ID
    ORDER BY A.Adresse_ID
    
    3	Yoda	NULL	NULL
    1	Han Solo	1	Corellia
    2	Chewbacca	4	Kashyyyk
    geht natürlich nicht, müsste ja vor dem GROUP BY sortiert werden. Also mit Subquery

    Code:
    SELECT
    	B.Benutzer_ID, B.Name, A.Adresse_ID, A.Strasse
    FROM Benutzer B
    LEFT JOIN Adresse A ON A.Benutzer_ID = B.Benutzer_ID
    WHERE
    	A.Adresse_ID = (
    		SELECT MAX(Adresse_ID)
    		FROM Adresse A2
    		WHERE A2.Benutzer_ID = B.Benutzer_ID
    	)
    
    1	Han Solo	3	Todesstern
    2	Chewbacca	4	Kashyyyk
    oh mann, das macht mir den LEFT JOIN, der ja alle Benutzer rausgeben sollte, wieder kaputt, also

    Code:
    SELECT
    	B.Benutzer_ID, B.Name, A.Adresse_ID, A.Strasse
    FROM Benutzer B
    LEFT JOIN Adresse A ON A.Benutzer_ID = B.Benutzer_ID
    WHERE
    	(
    		A.Adresse_ID = (
    			SELECT MAX(Adresse_ID)
    			FROM Adresse A2
    			WHERE A2.Benutzer_ID = B.Benutzer_ID
    		)
    		OR A.Adresse_ID IS NULL
    	)
    
    1	Han Solo	3	Todesstern
    2	Chewbacca	4	Kashyyyk
    3	Yoda	NULL	NULL
    OK, sieht gut aus. Bei den paar Datensätzen ist das OK - ich hab das mal mit 200 Benutzern und 220 Adressen probiert - das dauert 2.5 Sekunden. Geht natürlich garnicht, da kommen noch mehr Datensätze rein und ich hätte noch viel mehr Tabellen zum joinen. Ich komm da nicht weiter, hat jemand einen Tipp? Auf der Suche nach "mysql join höchster Datensatz" etc. bin ich nur über die Subqueries gestolpert, vllt. fehlt mir ja nur das passende Stichwort.

    Das Ganze ohne "OR A.Adresse_ID IS NULL" läuft übrigens in 0.02 Sekunden durch. Aber ich will halt auch die Datensätze ohne Adresse, wie beim LEFT JOIN.

    Gruss, Christian

  • #2
    du könntest das Problem umgehen, indem du "unbekannte" Adressen nicht als NULL wertest, sondern eine "Dummy-Adresse" setzt - die kann ja dann Leere Straße, Leerer Ort sein ....Hauptsache es ist ein Eintrag (vielleicht Eintrag mit ID 0 in der Adressen-Tabelle) - du könntest dann gleich noch neue User mit default "Adresse 0" anlegen - damit ist automatisch immer eine Adresse vorhanden, du brauchst also nicht den Kunstweg um die NULL abzufangen und das Ding wird massiv schneller

    Kommentar


    • #3
      Das klingt nach einer guten Idee, ich habe auch schon überlegt die aktuellste Adresse in einer separaten Spalte zu markieren ("aktiv" o.Ä.), dann könnte ich mir das Subquery sparen und über WHERE filtern. Zur Not mach ich das auch im Programmcode, aber ich hoffe ja die Datenbank schafft das auch in praktikabler Zeit mit einem Kniff den ich vllt. noch nicht kenne. Ich hab einfach das Gefühl ich denke in die falsche Richtung. Danke erstmal für den Vorschlag.

      Kommentar


      • #4
        Dependent Subqueries mit Mysql sind immer ungünstig. Ich würde in die Tabelle Benutzer einfach eine Spalte mit der aktuelle AdresseId einfügen.

        Alternativ kannst du dir die Eigenheit von Mysql zu nutze machen das Mysql bei GROUP BY immer den ersten Datensatz nimmt. (Das Verhalten ist nicht definiert, in der Dokumentation wird davor sogar explizit gewarnt).

        Code:
        SELECT 
        ...
        FROM
        	benutzer LEFT JOIN
        	(
        		SELECT
        			*
        		FROM
        			(
        				SELECT
        					*
        				FROM
        					Adresse
        				ORDER BY
        					Adresse.Adresse_ID DESC
        			) AS temp
        		GROUP BY
        			temp.Benutzer_ID
        	) AS akt_adresse ON (benutzer.benutzer_id = akt_adresse.benutzer_id)

        Kommentar


        • #5
          Hi, also das mit dem JOIN auf eine temporäre Tabelle läuft echt gut, 0.01 Sekunden, damit kann ich erstmal arbeiten. Danke, so siehts im Moment aus

          Code:
          SELECT
          	*
          FROM Benutzer B
          LEFT JOIN (
          		SELECT
          			*
          		FROM
          			Adresse
          		ORDER BY Adresse_ID DESC
          	
          	) A ON A.Benutzer_ID = B.Benutzer_ID
          GROUP BY B.Benutzer_ID
          ORDER BY B.Benutzer_ID
          
          1	Han Solo	3	Todesstern
          2	Chewbacca	4	Kashyyyk
          3	Yoda	NULL	NULL

          Kommentar

          Lädt...
          X