왼쪽 조인이있는 상위 1


94

아래 쿼리가 주어지면 동일한 마커 키를 가진 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'

답변:


203

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를 참조 할 수 있습니다.


감사합니다 @Remus, 그것은 나를 도왔습니다.
Sarthak Shah

3

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

  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하여 시스템의 모든 사용자에 대한 레코드를 확인할 수 있습니다.


2

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

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


좋아, 이제 무슨 말인지 알 것 같아. 여전히이 작업을 수행 할 수 있어야합니다. 기본적으로 dps_markers 테이블에는 방지해야하는 외부 쿼리에서 중복을 일으키는 행이 두 개 이상있을 수 있습니다.
dstarh

1

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'

1
네, 방금 시도했지만 u.id가 하위 선택에 표시되지 않습니다. 다중 부분 식별자 "u.id"를 바인딩 할 수 없습니다.
dstarh 2010 년

1
WHERE um.profile_id = 'u162231993'하위 쿼리와 WHERE mbg.marker_value IS NOT NULL외부에 넣을 수 있습니다.
Damir Sudarevic 2010 년

1
profile_id는 알지 못하며 다른 조인에서 가져온 것입니다. 이것은 훨씬 더 큰 쿼리에서 추출되었습니다
dstarh

1
글쎄, 변수 @SearchFor = 'u162231993'를 사용한 다음에서 사용 WHERE하거나 다른 사람들이 돕고 시도 할 수 있도록 일부 데이터 및 테이블 구조를 게시하십시오.
Damir Sudarevic 2010 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.