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