Manon: IF Abfrage zur Auswahl von verschiedenen LEFT JOIN

Hallo Beisamen!

Ich habe eine Frage zum Thema mysql Ich habe mehrere Tabellen die ich über eine SELECT Anfrage ansteuere.

Dort greife ich über LEFT JOIN auf verschieden Tabellen

LEFT JOIN data_text aa ON a.typ=1 AND aa.id_field=a.id

LEFT JOIN data_text bb ON a.typ=2 AND bb.id_field=a.id

LEFT JOIN data_calc bb ON a.typ=3 AND bb.id_field=a.id

Kann ich, es irgendwie so zusammenfassen

WENN ( a.typ=1 OR a.typ=2 )

LEFT JOIN data_text aa ON aa.id_field=a.id

SONST...

LEFT JOIN data_calc bb ON bb.id_field=a.id

  1. Hallo Manon,

    nein, kannst Du nicht so einfach. Eine Tabellenreferenz ist in einem SQL Statement nicht variabel.

    Wenn Du aus data_text und data_calc gleichartige Spalten ausliest, dann könntest Du es so machen um unnötigen Join-Aufwand zu sparen:

    SELECT a.bla1, a.bla2, a.bla3, aa.foo, aa.bar
    FROM blabla a
         LEFT JOIN data_text aa ON aa.id_field=a.id 
    WHERE a.typ in (1, 2)
    
    UNION ALL
    
    SELECT a.bla1, a.bla2, a.bla3, cc.foo, cc.bar
    FROM blabla a 
         LEFT JOIN data_calc cc ON cc.id_field=a.id
    WHERE a.typ not in (1, 2)
    

    (den Alias a kannst Du in beiden Teilen unabhängig verwenden, das ist kein Problem. Grad probiert 😉 ).

    Rolf

    --
    sumpsi - posui - clusi
  2. Hallo Manon,

    eine Ergänzung: die UNION-Lösung ändert ggf. die Reihenfolge der Sätze. Wie genau, hängt vom Execution Plan ab, den dein SQL Server für deine LEFT JOIN Kette gewählt hat, aber ich nehme mal an, dass es vorher eine nested loop war wo er für jeden Satz aus der a Tabelle einen Seek in die abhängigen Tabellen gemacht hat. D.h. die Ergebnissätze waren in Reihenfolge der Sätze aus Tabelle a.

    Durch den UNION wird das getrennt, du bekommst zuerst alle Sätze mit typ=1 oder typ=2, und dann alle anderen. Wenn das stört, musst Du noch einen ORDER BY nachlegen (der wirkt auf das UNION-Ergebnis) - aber das sollte immer noch schneller sein als die unnötigen SEEKs in der LEFT JOIN Kette. Wenn sie denn überhaupt ausgeführt werden - ein (a.typ=1) in der ON Bedingung könnte ja dazu führen, dass er für a.typ≠1 den SEEK überspringt.

    Im Zweifelsfalls hilft da nur messen, EXPLAINs analyiseren und im Zweifelsfalls doch zur alten Lösung zurückkehren. Die ersten beiden LEFT JOIN kannst Du ja auf jeden Fall zusammenlegen, wenn Du im ON ein (a.typ=1 OR a.typ=2) AND a.id=aa.id_field verwendest.

    Vielleicht ist das schon die einfachste Lösung. Man baut ein System zu Optimierungszwecken nur dann gravierend um, wenn es nötig ist.

    Rolf

    --
    sumpsi - posui - clusi
    1. Danke dir Rolf,

      wenn ich deinen Vorschlag aufnehme, der super funktioniert, dann habe ich das Ergebnis anderes sortiert als bei meinem Ansatz. Aber wo setze ich den dann den ORDER, da ich ja QUASI zwei SELECT habe?

      Und meinst du jetzt

      LEFT JOIN data_text aa ON a.typ=1 AND aa.id_field=a.id

      LEFT JOIN data_text bb ON a.typ=2 AND bb.id_field=a.id

      LEFT JOIN data_calc cc ON a.typ=3 AND bb.id_field=a.id

      Schneller und einfacher wäre? Und wenn JA, gibt es die Möglichkeit, da ich ja im Normalfall nur ein Ergebnis bekomme die leeren zu löschen, oder sie einfach zusammenzuführen?

      Also aus aa.foo, bb.foo, cc.foo quasi nur foo

      1. Hallo Manon,

        den ORDER BY setzt Du ans Ende. Der wirkt auf die UNION, nicht nur auf einen SELECT.

        Aber wenn Du bei der LEFT JOIN Kette bleibst, dann solltest die JOINs für Typ=1 und Typ=2 zusammenlegen.

        SELECT a.bla1, a.bla2, a.bla3, ab.foo, ab.bar, cc.foo, cc.bar
        FROM blablabla a
             LEFT JOIN data_text ab
                     ON (a.typ=1 OR a.typ=2) AND ab.id_field=a.id
             LEFT JOIN data_calc cc
                     ON a.typ=3 AND cc.id_field=a.id
        

        Die Frage ist, worum es Dir bei der Optimierung geht. Findest Du die LEFT JOIN Kette einfach unschön? Oder ist es Dir wichtig, die Spalten ab.foo und cc.foo sowie ab.bar und cc.bar zu einer Spalte zusammenzulegen?

        Zusammenlegen kannst Du sie mit der COALESCE-Funktion, die liefert nämlich den ersten nicht-NULL Wert ihrer Argumente.

        SELECT a.bla1, a.bla2, a.bla3,
               COALESCE(ab.foo, cc.foo), 
               COALESCE(ab.bar, cc.bar)
        FROM blablabla a
             LEFT JOIN data_text ab
                     ON (a.typ=1 OR a.typ=2) AND ab.id_field=a.id
             LEFT JOIN data_calc cc
                     ON a.typ=3 AND cc.id_field=a.id
        

        Das funktioniert für diese Query, weil über die Abfrage des Typs immer nur einer der beiden LEFT JOIN einen Treffer liefern kann. COALESCE ist hier die kompakte Alternative zu CASE. Mit CASE wird es schon recht geschwätzig (ich halte es noch knapp, weil ich Typ=1 und Typ=2 im ELSE abhandle):

        SELECT a.bla1, a.bla2, a.bla3,
               CASE a.typ WHEN 3 cc.foo ELSE ab.foo END, 
               CASE a.typ WHEN 3 cc.bar ELSE ab.bar END
        FROM blablabla a
             LEFT JOIN data_text ab
                       ON (a.typ=1 OR a.typ=2) AND ab.id_field=a.id
             LEFT JOIN data_calc cc 
                       ON a.typ=3 AND cc.id_field=a.id
        

        Okay, der UNION ALL ist noch geschwätziger. Aber nach meiner Meinung ist es dann klarer, was passiert. Wie es mit der Performance aussieht, muss man wie gesagt messen. Da hängt viel am Datenvolumen und an vorhandenen Indexen.

        Rolf

        --
        sumpsi - posui - clusi
        1. Hallo, du meinst mit geschwätzig "braucht viel Performance?"

          Manon

          1. Hallo Manon,

            geschwätzig meint den SQL Text, d.h. es braucht viel Performance deinerseits beim Eintippen 😉.

            Längeres SQL muss nicht unbedingt länger laufen.

            Rolf

            --
            sumpsi - posui - clusi