web-dev-qa-db-fra.com

Top 1 avec une jointure à gauche

Étant donné la requête ci-dessous, il pourrait y avoir plusieurs lignes dans dps_markers avec la même clé de marqueur, mais nous souhaitons uniquement nous joindre à la première. Si je prends cette requête et supprime le top 1 et ORDER BY, j'obtiens une valeur pour mbg.marker_value mais je l'exécute telle quelle, elle renvoie toujours null

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'
80
dstarh

Utilisez OUTER APPLY au lieu de 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';

A la différence de JOIN, APPLY vous permet de référencer le u.id dans la requête interne.

176
Remus Rusanu

La clé de ce type de débogage consiste à exécuter la vue sous-requête/en ligne seule pour voir quelle est la sortie:

  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

En exécutant cela, vous verriez que le profile_id La valeur ne correspond pas à la u.id valeur de u162231993, ce qui expliquerait pourquoi toutes les références mbg renvoient null (grâce à la jointure gauche; vous n’obtenez rien si c’était une jointure interne).

Vous vous êtes codé dans un coin en utilisant TOP, car vous devez maintenant modifier la requête si vous souhaitez l'exécuter pour d'autres utilisateurs. Une meilleure approche serait:

   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'

Avec cela, vous pouvez modifier la valeur id de la clause where pour vérifier les enregistrements de tout utilisateur du système.

2
OMG Ponies

Parce que le TOP 1 de la sous-requête ordonnée n'a pas profile_id = 'u162231993' Retirer where u.id = 'u162231993' et voir les résultats alors.

Exécutez la sous-requête séparément pour comprendre ce qui se passe.

1
Damir Sudarevic

Damir a raison,

Votre sous-requête doit vous assurer que dps_user.id est égal à um.profile_id. Sinon, il prendra la ligne du haut qui pourrait, mais ne correspondra probablement pas à votre identifiant "u162231993".

Votre requête devrait ressembler à ceci:

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'
0
Nathan Koop