Top 1 mit einem linken Join

91

In Anbetracht der folgenden Abfrage gibt es möglicherweise mehrere Zeilen in dps_markers mit demselben Markierungsschlüssel, aber wir möchten nur gegen die erste beitreten. Wenn ich diese Abfrage nehme und die Top 1 und ORDER BY entferne, erhalte ich einen Wert für mbg.marker_value, aber laufe so wie er ist, gibt er immer null zurück

SELECT u.id, mbg.marker_value 
FROM dps_user u
LEFT JOIN 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     ORDER BY m.creation_date
    ) MBG ON MBG.profile_id=u.id 
WHERE u.id = 'u162231993'
dstarh
quelle

Antworten:

195

Verwenden Sie OUTER APPLY anstelle von LEFT JOIN:

SELECT u.id, mbg.marker_value 
FROM dps_user u
OUTER APPLY 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     WHERE um.profile_id=u.id 
     ORDER BY m.creation_date
    ) AS MBG
WHERE u.id = 'u162231993';

Im Gegensatz zu JOIN können Sie mit APPLY auf die u.id in der inneren Abfrage verweisen.

Remus Rusanu
quelle
Danke @Remus, es hat mir geholfen.
Sarthak Shah
2

Der Schlüssel zum Debuggen solcher Situationen besteht darin, die Unterabfrage / Inline-Ansicht selbst auszuführen, um zu sehen, wie die Ausgabe lautet:

  SELECT TOP 1 
         dm.marker_value, 
         dum.profile_id
    FROM DPS_USR_MARKERS dum (NOLOCK)
    JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                AND dm.marker_key = 'moneyBackGuaranteeLength'
ORDER BY dm.creation_date

Wenn Sie dies ausführen, werden Sie feststellen, dass der profile_idWert nicht mit dem u.idWert von übereinstimmt. Dies u162231993würde erklären, warum mbgReferenzen zurückgegeben werden null(dank des linken Joins; Sie würden nichts erhalten, wenn es sich um einen inneren Join handelt).

Sie haben sich mit in eine Ecke codiert TOP, da Sie jetzt die Abfrage optimieren müssen, wenn Sie sie für andere Benutzer ausführen möchten. Ein besserer Ansatz wäre:

   SELECT u.id, 
          x.marker_value 
     FROM DPS_USER u
LEFT JOIN (SELECT dum.profile_id,
                  dm.marker_value,
                  dm.creation_date
             FROM DPS_USR_MARKERS dum (NOLOCK)
             JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                         AND dm.marker_key = 'moneyBackGuaranteeLength'
           ) x ON x.profile_id = u.id
     JOIN (SELECT dum.profile_id,
                  MAX(dm.creation_date) 'max_create_date'
             FROM DPS_USR_MARKERS dum (NOLOCK)
             JOIN DPS_MARKERS dm (NOLOCK) ON dm.marker_id= dum.marker_id 
                                         AND dm.marker_key = 'moneyBackGuaranteeLength'
         GROUP BY dum.profile_id) y ON y.profile_id = x.profile_id
                                   AND y.max_create_date = x.creation_date
    WHERE u.id = 'u162231993'

Damit können Sie den idWert in der whereKlausel ändern , um Datensätze für jeden Benutzer im System zu überprüfen.

OMG Ponys
quelle
1

Weil die TOP 1aus der bestellten Unterabfrage nicht profile_id = 'u162231993' entfernen where u.id = 'u162231993'und dann Ergebnisse sehen.

Führen Sie die Unterabfrage separat aus, um zu verstehen, was los ist.

Damir Sudarevic
quelle
Ok, ich glaube ich verstehe was du jetzt meinst. müssen noch in der Lage sein, dies zum Laufen zu bringen. Grundsätzlich kann die Tabelle dps_markers mehr als eine Zeile enthalten, was zu Dupes in der äußeren Abfrage führt, die wir verhindern müssen.
Dstarh
0

Damir ist richtig,

Ihre Unterabfrage muss sicherstellen, dass dps_user.id gleich um.profile_id ist. Andernfalls wird die oberste Zeile erfasst, die möglicherweise, aber wahrscheinlich nicht Ihrer ID von 'u162231993' entspricht.

Ihre Anfrage sollte folgendermaßen aussehen:

SELECT u.id, mbg.marker_value 
FROM dps_user u
LEFT JOIN 
    (SELECT TOP 1 m.marker_value, um.profile_id
     FROM dps_usr_markers um (NOLOCK)
         INNER JOIN dps_markers m (NOLOCK) 
             ON m.marker_id= um.marker_id AND 
                m.marker_key = 'moneyBackGuaranteeLength'
     WHERE u.id = um.profile_id
     ORDER BY m.creation_date
    ) MBG ON MBG.profile_id=u.id 
WHERE u.id = 'u162231993'
Nathan Koop
quelle
Ja, ich habe das gerade versucht, aber u.id ist in der Unterauswahl nicht sichtbar. Die mehrteilige Kennung "u.id" konnte nicht gebunden werden.
Dstarh
Sie können WHERE um.profile_id = 'u162231993'in die Unterabfrage und WHERE mbg.marker_value IS NOT NULLnach außen setzen.
Damir Sudarevic
Ich werde die profile_id nicht konw, es wird von einem anderen Join sein. Dies wurde aus einer viel größeren Abfrage herausgezogen
dstarh
Verwenden Sie die Variable @SearchFor = 'u162231993'und verwenden Sie sie dann in WHEREoder veröffentlichen Sie einige Daten- und Tabellenstrukturen, damit andere Personen helfen und sie ausprobieren können.
Damir Sudarevic