具有最新记录的SQL子查询

时间:2019-06-29 00:53:48

标签: sql sql-server sql-server-2008

我在这里已经阅读了几乎所有的问题,可以发现这是从子查询中获取最新记录,但是我无法弄清楚如何使其在我的情况下起作用。

我正在创建要在SQL Server 2008上使用的SSRS报告。

数据库中是联系人和DBSdata的表。我想从DBSdata表(将来最远的到期日期)中提取联系人列表和最新记录(该行中的许多字段)

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表没有它自己的主键字段-不幸的是,这不是我可以控制的...而且每个联系人的顺序数增加,所以FKContactID + Ordinal是唯一的。...]

这是我必须使用的代码,但是它不起作用。我要上载SSRS的系统根本没有给我任何有用的错误消息,因此恐怕我无法更详细地说明哪些功能无效。我没有显示任何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有关,但是如果我没有这个问题,则整个表正在使用WHOLE表并返回1行,而不是该联系人的前1行。

3 个答案:

答案 0 :(得分:1)

您的方法将使用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实现了称为“横向连接”的功能。这就像一个相关的子查询,但是它可以返回多行多列。横向联接非常强大,这是使用它们的一个很好的例子。

为了提高性能,您希望在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子句中。

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

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之前执行子查询为优化器提供了更多机会来制定更好的执行计划。

答案 1 :(得分:0)

以下是使用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

答案 2 :(得分:0)

此解决方案可以提供您所期望的结果,并且性能更高。

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