SQL左边连接重复

时间:2015-03-19 09:32:19

标签: sql-server sql-server-2012

我有以下表格结构:

enter image description here

我无法使用左连接获得上述所需的输出:

SELECT K.[ID], 
       K.[KeyName], 
       K.Notes, 
       KR.isSignedIn
  FROM [dbo].[Keys] K
  LEFT JOIN 
           [dbo].[KeyRecords] KR WITH(NOLOCK) 
  ON
     K.ID = KR.Key_ID
  WHERE
        (KR.isSignedIn = 1 OR KR.isSignedIn is null) 

我知道原因,这是因为KeyRecords表可以为同一个键提供多个条目。但是如何让它只检查该密钥的最后一个条目?

所以我希望Key表中的所有记录在记录表中具有最后一条记录isSignedIn = 1。如果KeyRecords表中没有密钥记录,我仍然想要显示它。

2 个答案:

答案 0 :(得分:0)

这不是最好的解决方案,但这就是诀窍 我不知道你的桌子有多大,或者你的桌子上有任何索引。

select 
  k.ID, k.KeyName, K.Notes, KR.isSignedIn
from dbo.[Key] K
left join dbo.[KeyRecords] KR
  on kr.Key_ID = k.ID
where (
       select MAX(ID)
       from dbo.[KeyRecords] sub
       where sub.Key_ID = kr.Key_ID
      ) = 
      (
       select ID 
       from dbo.[KeyRecords] sub
       where sub.Key_ID = kr.Key_ID
       and sub.isSignedIn = 1
      )
or kr.ID is null

请查看SQLFiddle

答案 1 :(得分:0)

当您需要“密钥的最后一个条目”时,请使用CROSS or OUTER APPLY

WITH
CTE
AS
(
  SELECT * 
  FROM
    dbo.[Key] AS K
    OUTER APPLY
    (
      SELECT TOP(1) KeyRecords.isSignedIn
      FROM KeyRecords
      WHERE KeyRecords.Key_ID = K.ID
      ORDER BY KeyRecords.ID DESC
    ) AS A
)
SELECT *
FROM CTE
WHERE isSignedIn = 1 OR isSignedIn IS NULL
ORDER BY ID

这是SQLFiddle

CTE的作用:对于每个Key,找到KeyRecord中具有最大ID的行。 (如果此行不存在,请返回NULL,因此我们需要OUTER APPLY

然后进一步过滤结果,删除那些isSignedIn = 0

如果您在KeyRecords (Key_ID, ID)上有小表或索引,则此变体可以有效运行。您很可能已经在ID上拥有主键,因此Key_ID上包含isSignedIn列的简单索引就足够了。

Execution plan

您可以从计划中看到,对于Key表中的每一行,服务器在KeyRecords表上执行一次索引查找。如果Key表格很小且KeyRecords很大,则整体效率非常高。