我可以使用Top 1和Order By作为加入的一部分吗?

时间:2013-06-26 12:55:14

标签: sql sql-server-2008

我试图做以下事情。我需要返回一个人记录,其中还包括该人最老的狗的名字(当一个人有> 1只狗的时候)。以下查询返回NULL dogName。 (我已经检查并且有有效数据)如果我删除了联接查询的Top 1Order by部分,则会返回有效的dogName,但不会返回&{39}。最古老的'狗。为什么添加TOP 1OrderBy会导致它返回NULL?

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'

注意:这只是一个类似的查询,我不允许在论坛中发帖。

另外,我不是一个专家,所以我可能试图错误地解决这个问题。提前谢谢。

7 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

您的ORDER BY子句似乎需要有一个尾随的DESC关键字;正如你所指出的,你想要找回“最老的狗”。

答案 2 :(得分:0)

对我来说没问题,虽然在这种情况下我可能会考虑Rank()Over()

虽然在这种情况下可能会慢一些

答案 3 :(得分: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'

答案 4 :(得分:0)

你的查询返回NULL的原因是你从桌子上挑选了一只狗,而不是每人一只狗1只狗,所以如果'john'没有最年轻的狗(因为你没有按降序排序)狗表,你不会返回一个值。您首先需要每个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()

答案 5 :(得分: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'

答案 6 :(得分: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'