LEFT OUTER JOIN并且仅返回第一场比赛

时间:2016-03-15 16:57:06

标签: sql sql-server left-join outer-join

想象一下以下两个表,分别命名为“Users”和“Orders”:

ID  NAME
1   Foo
2   Bar
3   Qux


ID  USER  ITEM  SPEC  TIMESTAMP
1   1     12    4     20150204102314
2   1     13    6     20151102160455
3   3     25    9     20160204213702

我希望得到的输出是:

USER   ITEM  SPEC  TIMESTAMP
1      12    4     20150204102314
2      NULL  NULL  NULL
3      25    9     20160204213702

换句话说:在用户和订单之间进行LEFT OUTER JOIN,如果您没有找到该用户的任何订单,则返回null,但如果找到一些,则只返回第一个(最早的一个)在时间戳上。)

如果我只使用LEFT OUTER JOIN,它将为用户1返回两行,我不希望这样。我想把LEFT OUTER JOIN嵌套在另一个选择中,它将GROUP BY其他字段并获取MIN(TIMESTAMP),但这不起作用,因为我需要在我的组中有“SPEC”,并且因为这两个命令有不同的规格,它们仍然出现。

对于如何实现理想结果的任何想法都表示赞赏。

4 个答案:

答案 0 :(得分:3)

我能想到的最好方法是使用OUTER APPLY

SELECT *
FROM   Users u
       OUTER apply (SELECT TOP 1 *
                    FROM   Orders o
                    WHERE  u.ID = o.[USER]
                    ORDER  BY TIMESTAMP DESC) ou

此外,在NON-Clustered表格上创建以下ORDERS索引将有助于您提高查询效果

CREATE NONCLUSTERED INDEX IX_ORDERS_USER
  ON ORDERS ([USER], TIMESTAMP)
  INCLUDE ([ITEM], [SPEC]); 

答案 1 :(得分:2)

这应该可以解决问题:

SELECT  Users.ID, Orders2.USER ,  Orders2.ITEM ,  Orders2.SPEC ,  Orders2.TIMESTAMP
FROM    Users
LEFT JOIN 
        (
        SELECT  Orders.ID, Orders.USER ,  Orders.ITEM ,  Orders.SPEC ,  Orders.TIMESTAMP, ROW_NUMBER()
                OVER (PARTITION BY ID  ORDER BY TIMESTAMP DESC) AS RowNum
        FROM    Orders

        ) Orders2 ON Orders2.ID = Users.ID And RowNum = 1

答案 2 :(得分:1)

另一种方法是使用窗口函数作为Cte:

with Sorted as
(
  select u.id as User, o.Item, o.Spec, o.Timestamp
         row_number() over (partition by u.Id order by Timestamp) as Row
    from Users u
    left join orders o
      on o.User = u.Id
)
select User, Item, Spec, Timestamp
  from Sorted where Row = 1

答案 3 :(得分:1)

您会在this question找到很多建议。你有一个左连接的事实是你想要做的事情的偶然事件,所以这些答案应该很容易适应你的问题。我同意@MotoGP,对于SQLServer,OUTER APPLY可能是最好的方法。它与Postgres的LATERAL JOIN非常相似(在另一个链接中提到)。