LEFT OUTER JOIN 并且只有 return 第一个匹配项

LEFT OUTER JOIN and only return the first match

设想以下两个表,分别命名为 "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

换句话说:在 Users 和 Orders 之间做一个 LEFT OUTER JOIN,如果你没有找到那个用户的任何订单,return null,但如果你找到了一些,只有 return第一个(根据时间戳最早的一个)。

如果我只使用 LEFT OUTER JOIN,它将 return 用户 1 的两行,我不希望这样。我想将 LEFT OUTER JOIN 嵌套在另一个 select 中,它将 GROUP BY 其他字段并获取 MIN(TIMESTAMP) 但这也不起作用,因为我需要在我的组中有 "SPEC" by ,并且由于这两个订单具有不同的规格,它们仍然会同时出现。

如有任何关于如何实现预期结果的想法,我们将不胜感激。

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

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

另外在 ORDERS table 上创建 NON-Clustered 索引将帮助您提高查询的性能

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

另一种方法是使用窗口函数作为 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

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

这应该可以解决问题:

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