2013-06-26 41 views
0

我正在嘗試做下面的事情。我需要返回一個人的記錄,該記錄還包括該人最古老的狗的名字(因爲當一個人有一條狗時)。以下查詢返回NULLdogName。 (我已經檢查並且有有效的數據)如果我刪除Top 1Order by部分連接查詢,它將返回一個有效的dogName,但不是「最老的」狗。爲什麼添加TOP 1OrderBy會導致它返回NULL?我可以使用Top 1和Order By作爲連接的一部分嗎?

SELECT 
    pt.firstName [FirstName], 
    pt.lastName [LastName], 
    joinQuery.dogName [dogName] 
FROM dbo.PersonTable pt 
LEFT OUTER JOIN 
    (
     SELECT 
      TOP 1 
      dt.dogName [dogName], 
      dt.dogAge [dogAge] 
     FROM 
      DogTable dt 
     ORDER BY dt.dogAge 
    ) joinQuery ON joinQuery.PersonId = pt.Id 
WHERE 
    pt.firstName = 'john' 

注意:這只是一個類似的查詢,我不允許在論壇發帖。

此外,我不是任何方式的專家,所以我可能會嘗試錯誤地解決此問題。提前致謝。

+1

注意,最長壽的狗是'ORDER BY dt.dogage DESC'。 –

+0

根據有效數據,誰擁有最古老的狗? –

+1

你真的運行過這個查詢嗎?子查詢甚至沒有'PersonId'作爲返回的列。 – Taryn

回答

0

看來你的ORDER BY子句需要有一個尾隨DESC關鍵字;正如你所指出的那樣,你想檢索「最古老的狗」。

0

看起來不錯對我來說,雖然我可能會在這種情況下考慮RANK()OVER()

儘管它可能是在這種情況下慢

1

你並不需要一個連接都:

SELECT pt.firstname    [FirstName], 
     pt.lastname    [LastName], 
     (SELECT TOP 1 dt.dogname 
     FROM dogtable dt 
     WHERE dt.personid = pt.id 
     ORDER BY dt.dogage DESC) AS [dogName] 
FROM dbo.persontable pt 
WHERE pt.firstname = 'john' 

請注意,最古老的狗是ORDER BY dt.dogage DESC

+0

+1爲簡單起見。 – xagyg

0

您的查詢無效,您正在加入的列不是從DogTable中選擇的,也是如果您想要最早的,您可以使用order by desc或使用max函數。

SELECT 
    pt.firstName [FirstName], 
    pt.lastName [LastName], 
    joinQuery.dogName [dogName] 
FROM dbo.PersonTable pt 
LEFT OUTER JOIN 
    (
     SELECT 
      TOP 1 
      dt.PersonId, 
      dt.dogName [dogName], 
      dt.dogAge [dogAge] 
     FROM 
      DogTable dt 
     ORDER BY dt.dogAge 
    ) joinQuery ON joinQuery.PersonId = pt.Id 
WHERE 
    pt.firstName = 'john' 
0

查詢返回NULL的原因是,你從桌上拿起一隻狗,每PERSONID不是一隻狗,所以如果「約翰」沒有最年輕的(因爲你沒有通過dogage訂購下降)在表中,你不會返回一個值。你需要每一個是PersonID結果,然後再限制到只有人/人,你有興趣

使用ROW_NUMBER()RANK()是問題,像這樣的一個很好的解決方案:

SELECT 
    pt.firstName [FirstName], 
    pt.lastName [LastName], 
    pet.dogName [dogName] 
FROM dbo.PersonTable pt 
LEFT JOIN 
    (
     SELECT dogName [dogName],dogAge [dogAge], PersonID, ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY dogage DESC) DogRank 
     FROM DogTable 
    ) pet ON pet.PersonId = pt.Id 
    AND pet.DogRank = 1 
WHERE 
    pt.firstName = 'john' 

如果一個人有多個6歲的狗,你可以使用RANK()而不是ROW_NUMBER()返回兩隻狗。如果你只想要其中的一個,堅持ROW_NUMBER()

0

溝槽TOPORDER BY並且不使用任何花哨的SQL(PARTITIONSRANK等等)。只需使用普通的舊SQL,像這樣...

SELECT 
    pt.firstName [FirstName], 
    pt.lastName [LastName], 
    joinQuery.dogName [dogName] 
FROM dbo.PersonTable pt 
LEFT OUTER JOIN dbo.DogTable joinQuery ON joinQuery.PersonId = pt.id 
WHERE joinQuery.dogAge = (SELECT MAX(dt.dogAge) 
          FROM DogTable dt WHERE dt.PersonId = pt.id) 
AND pt.firstName = 'john' 
+0

我猜ROW_NUMBER()和RANK()可能是「看中」了一些,但它們允許比你有這裏發生了什麼更高的效率。 –

+0

@Goat_CO沒有難過的感覺。我更喜歡(到目前爲止)標準SQL。 – xagyg

0

另一種方法是使用APPLYOUTER APPLY對應於外連接,並且CROSS APPLY對應於內連接。見

http://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/

http://technet.microsoft.com/en-us/library/ms175156%28v=sql.105%29.aspx

SELECT 
    pt.firstName [FirstName], 
    pt.lastName [LastName], 
    joinQuery.dogName [dogName] 
FROM dbo.PersonTable pt 
OUTER APPLY 
    (
     SELECT 
      TOP 1 
      dt.dogName [dogName], 
      dt.dogAge [dogAge] 
     FROM 
      DogTable dt 
     WHERE dt.PersonId = pt.Id 
     ORDER BY dt.dogAge 
    ) joinQuery 
WHERE 
    pt.firstName = 'john' 
相關問題