左結合のトップ1


92

以下のクエリを考えると、同じマーカーキーを持つ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'

回答:


196

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

2

このような状況をデバッグするための鍵は、サブクエリ/インラインビューを単独で実行して、出力を確認することです。

  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.id値と一致しないことがわかりますu162231993。これは、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'

これによりidwhere句の値を変更して、システム内の任意のユーザーのレコードを確認できます。


1

のでTOP 1注文したサブクエリからは持っていないprofile_id = 'u162231993' の削除をwhere u.id = 'u162231993'して、結果を参照してください。

何が起こっているのかを理解するために、サブクエリを個別に実行します。


わかりました、私はあなたが今何を言っているのかわかります。これを機能させる必要があります。基本的に、私はテーブルdps_markersテーブルに複数の行がある可能性があり、これにより、外部クエリで重複を防ぎ、防止する必要があります。
dstarh

0

ダミールは正しいです

サブクエリでは、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'

うん、試してみただけですが、サブ選択にu.idが表示されていませんマルチパート識別子「u.id」をバインドできませんでした。
dstarh

あなたはWHERE um.profile_id = 'u162231993'サブクエリとWHERE mbg.marker_value IS NOT NULL外側に置くことができます。
Damir Sudarevic、2010年

私はprofile_idを知りません、それは別の参加からのものです。これは、はるかに大きなクエリから除外されました
dstarh

変数@SearchFor = 'u162231993'を使用してからで使用するWHEREか、他の人が助けて試せるように、いくつかのデータとテーブル構造を投稿します。
Damir Sudarevic、2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.