[sql-server] 왼쪽 조인이있는 상위 1

아래 쿼리가 주어지면 동일한 마커 키를 가진 dps_markers에 여러 행이있을 수 있지만 첫 번째 행에 대해서만 조인하려고합니다. 이 쿼리를 가져 와서 상위 1 개와 ORDER BY를 제거하면 mbg.marker_value에 대한 값이 있지만 그대로 실행하면 항상 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'



답변

LEFT JOIN 대신 OUTER APPLY 사용 :

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';

JOIN과 달리 APPLY를 사용하면 내부 쿼리 내에서 u.id를 참조 할 수 있습니다.


답변

이와 같은 상황을 디버깅하기위한 핵심은 하위 쿼리 / 인라인 뷰를 자체적으로 실행하여 출력이 무엇인지 확인하는 것입니다.

  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

그 실행, 당신은 볼 것이다 profile_id값이 일치하지 않는 u.idu162231993어떤 이유를 설명하는 것, mbg참조가 반환 null(왼쪽 덕분에 조인, 그것은 내부 조인한다면 당신은 아무것도 얻을 수 없겠죠).

이제 TOP다른 사용자를 위해 쿼리를 실행하려면 쿼리를 조정해야하므로를 사용하여 자신을 코너로 코딩 했습니다. 더 나은 접근 방식은 다음과 같습니다.

   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'

이를 통해 절의 id값을 변경 where하여 시스템의 모든 사용자에 대한 레코드를 확인할 수 있습니다.


답변

때문에 TOP 1주문한 하위 쿼리가없는 profile_id = 'u162231993'
제거를 where u.id = 'u162231993'한 후 결과를 참조하십시오.

무슨 일이 일어나고 있는지 이해하기 위해 하위 쿼리를 별도로 실행하십시오.


답변

Damir가 맞습니다.

하위 쿼리는 dps_user.id가 um.profile_id와 일치하는지 확인해야합니다. 그렇지 않으면 ‘u162231993’의 ID와 같지 않을 수도있는 맨 위 행을 가져옵니다.

쿼리는 다음과 같아야합니다.

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'


답변