Подзапрос SQL с последней записью - PullRequest
4 голосов
/ 29 июня 2019

Я прочитал почти каждый вопрос здесь, который я могу найти, который ссылается на получение последней записи из подзапроса, но я просто не могу понять, как заставить это работать в моей ситуации.

Я создаю отчет SSRS для использования в SQL Server 2008.

В базе данных есть таблица контактов и 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 в подзапросе, но если у меня его нет, вся эта таблица использует ВЕСЬ таблицу и возвращает 1 строку, а не верхнюю 1 для этого контакта.

Ответы [ 3 ]

1 голос
/ 29 июня 2019

Ваш метод будет работать с использованием 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 дает оптимизатору больше возможностей для создания лучшего плана выполнения.

0 голосов
/ 29 июня 2019

Это решение дает ожидаемый результат, а производительность намного выше.

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
0 голосов
/ 29 июня 2019

Вот один из вариантов использования 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...