0

我正在尝试执行以下操作。我需要返回一个人记录,其中还包括那个人最老的狗的名字(当一个人有 >1 条狗时)。下面的查询返回NULL. dogName(我已经检查过并且有有效的数据)如果我删除连接查询的Top 1andOrder by部分,它会返回一个有效的dogName,但不是“最古老的”狗。为什么添加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'

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

另外,我无论如何都不是专家,所以我可能试图错误地解决这个问题。提前致谢。

4

7 回答 7

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.

于 2013-06-26T13:01:01.620 回答
0

您的查询返回 NULL 的原因是您从表中挑选了 1 只狗,而不是每个 personID 1 只狗,所以如果“john”在表中没有最年轻的狗(因为您没有按狗降序排序),你不会返回一个值。您首先需要每个 PersonID 的结果,然后仅限于您感兴趣的人/人。

使用ROW_NUMBER()orRANK()是解决此类问题的好方法:

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().

于 2013-06-26T13:02:49.327 回答
0

另一种方法是使用APPLY. OUTER 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'
于 2013-06-26T13:14:20.103 回答
0

抛弃TOPandORDER BY并且不要使用任何花哨的 SQL(PARTITIONS,RANK等)。只需使用普通的旧 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'
于 2013-06-26T13:08:10.247 回答
0

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

于 2013-06-26T12:59:19.360 回答
0

对我来说看起来不错,尽管在这种情况下我可能会考虑 Rank() Over()

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

于 2013-06-26T13:00:19.167 回答
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'
于 2013-06-26T13:01:10.497 回答