SQL 有最新记录的子查询

SQL subquery with latest record

我已经阅读了这里的几乎所有问题,我发现它引用了从子查询中获取最新记录,但我不知道如何让它在我的情况下工作。

我正在创建用于 SQL Server 2008 的 SSRS 报告。

数据库中有 table 联系人和 DBS 数据。我想从 DBSdata table(最晚到期日)

中提取联系人列表和最新记录(该行的许多字段)
Contacts
========
PKContactID      ContactName
-----------      -----------
1                JONES Chris
2                SMITH Mary
3                GREY Jean


DBSdata
=======
Ordinal   FKContactID   ExpiryDate   IssueDate     DBSType
-------   -----------   ----------   ---------     -------
3         1             2021-09-01   2019-09-01    Internal
2         1             2019-08-31   2017-08-31    External
1         1             2017-07-01   2015-07-01    Internal
2         2             2021-04-15   2019-04-15    Internal
1         2             2019-05-05   2017-05-06    External
1         3             2018-01-03   2016-03-02    External

我想要的结果是:

Latest DBS
==========
PKContactID   ContactName      ExpiryDate    IssueDate     DBSType
-------------------------------------------------------------------
3             GREY Jean        2018-01-03    2016-03-02    External
1             JONES Chris      2021-09-01    2019-09-01    Internal
2             SMITH Mary       2021-04-15    2019-04-15    Internal

[DBSData table 没有它自己的主键字段 - 不幸的是,这不是我可以控制的东西......而且每个联系人的序数增加,所以 FKContactID+Ordinal 是唯一的.. ..]

这是我必须使用的代码,但它不起作用。我将 SSRS 上传到的系统根本没有给我任何有用的错误消息,所以恐怕我不能更具体地说明什么不起作用。我得到 none 的 SSRS 报告显示,只是一个错误说数据集源不工作。

    SELECT
        c.PKContactID, c.ContactName, d.ExpiryDate, d.IssueDate, d.DBSType
    FROM
        Contacts c
        LEFT JOIN (
                      SELECT TOP 1 FKContactID, ExpiryDate, IssueDate, DBSType
                      FROM DBSData
                      WHERE FKContactID = c.PKContactID
                      ORDER BY ExpiryDate DESC
                  ) d ON c.PKContactID = d.FKContactID
    ORDER BY
       c.ContactName

我怀疑这与子查询中的 WHERE 有关,但如果我没有,整个 table 正在使用整个 table 并返回 1行,而不是该联系人的前 1 位。

这是一个使用 row_number() 的选项:

SELECT * 
FROM (
    SELECT
        c.PKContactID, c.ContactName, d.ExpiryDate, d.IssueDate, d.DBSType, 
        row_number() over (partition by c.PKContactID order by d.ExpiryDate desc) rn
    FROM
        Contacts c
        LEFT JOIN DBSData d ON d.FKContactID = c.PKContactID
) t
WHERE rn = 1
ORDER BY ContactName

此解决方案给出了您预期的结果,而且性能要高得多。

select c.PKContactID,c.ContactName,d.ExpiryDate, d.IssueDate, d.DBSType from Contacts c
inner join DBSdata d
on c.PKContactID=d.FKContactID
where d.Ordinal in (select max(d.Ordinal) from DBSdata d where d.FKContactID=c.PKContactID)
order by c.ContactName

您的方法可以使用 APPLY,而不是 JOIN:

SELECT c.PKContactID, c.ContactName,
       d.ExpiryDate, d.IssueDate, d.DBSType
FROM Contacts c OUTER APPLY
     (SELECT TOP 1 d.*
      FROM DBSData d
      WHERE d.FKContactID = c.PKContactID
      ORDER BY d.ExpiryDate DESC
     ) d
ORDER BY c.ContactName;

技术上 APPLY 实现了一种称为 横向连接 的东西。这就像一个相关子查询,但它可以 return 多行和多列。横向连接非常强大,这是使用它们的一个很好的例子。

为了性能,您需要 DBSData(FKContactID, ExpiryDate DESC) 上的索引(也许还包括您想要的其他列)和 Contacts(ContactName).

有了正确的索引,我希望它的性能至少与其他方法一样好。

另一种通常也具有良好性能的替代方法是使用相关子查询进行过滤:

SELECT c.PKContactID, c.ContactName,
       d.ExpiryDate, d.IssueDate, d.DBSType
FROM Contacts c LEFT JOIN
     DBSData d
     ON d.FKContactID = c.PKContactID AND
        d.ExpiryDate = (SELECT MAX(d2.ExpiryDate)
                        FROM DBSData d
                        WHERE d2.FKContactID = d.FKContactID
                       );

注意,要匹配LEFT JOIN,关联条件需要在ON子句中,而不是WHERE子句中。

最后,如果您使用 window 函数,我建议使用子查询来获取第一行:

SELECT c.PKContactID, c.ContactName,
       d.ExpiryDate, d.IssueDate, d.DBSType
FROM Contacts c LEFT JOIN
     (SELECT d.*,
             ROW_NUMBER() OVER (PARTITION BY d.FKContactID ORDER BY d.PKContactID DESC) as seqnum
      FROM DBSData d
     ) d
     ON d.FKContactID = c.PKContactID AND
        d.seqnum = 1;

JOIN 之前执行子查询可以让优化器有更多机会生成更好的执行计划。