2016-03-15 355 views
1

試想以下兩個表,一個名爲「用戶」及「訂單」:LEFT OUTER JOIN和只返回第一個匹配

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返回兩行,我不希望這樣。我想在另一個select中嵌套LEFT OUTER JOIN,GROUP BY其他字段並獲取MIN(TIMESTAMP),但這不起作用,因爲我需要在我的組中有「SPEC」,並且由於這兩個命令有不同的SPEC,他們仍然出現。

有關如何達到預期結果的任何想法,

回答

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 

另外創建於ORDERS表下方NON-Clustered指數將幫助您提高查詢

CREATE NONCLUSTERED INDEX IX_ORDERS_USER 
    ON ORDERS ([USER], TIMESTAMP) 
    INCLUDE ([ITEM], [SPEC]); 
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 
1

你會在this question找到很多建議。你有一個左連接的事實是你想要做的事情的附帶,所以這些答案應該很容易適應你的問題。我同意@MotoGP對於SQLServer OUTER APPLY可能是最好的方法。這與Postgres的LATERAL JOIN(在其他鏈接中提到)非常相似。

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