左联接的前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'

Answers:


196

使用OUTER APPLY代替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';

与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 2010年

0

达米尔是对的,

您的子查询需要确保dps_user.id等于um.profile_id,否则它将抢占第一行,该行可能但可能不等于您的ID'u162231993'

您的查询应如下所示:

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 2010年

您可以将WHERE um.profile_id = 'u162231993'子查询WHERE mbg.marker_value IS NOT NULL放在外部。
Damir Sudarevic

我不会知道profile_id,它将来自另一个联接。这是从更大的查询中
删除的

很好,请使用变量@SearchFor = 'u162231993',然后在中使用该变量WHERE,或发布一些数据和表结构,以便其他人可以提供帮助并进行尝试。
Damir Sudarevic 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.