нужно получить последующую запись с конкретным значением - PullRequest
0 голосов
/ 23 мая 2019

у меня есть следующая таблица:

        Dt          Status
05.23.2019 10:00:00   A
05.23.2019 11:00:00   B
05.23.2019 12:00:00   B
05.23.2019 13:00:00   D
05.23.2019 14:00:00   A
05.23.2019 15:00:00   B
05.23.2019 16:00:00   C
05.23.2019 17:00:00   D
05.23.2019 18:00:00   A

Для каждого статуса A мне нужно получить следующий статус D. Результат должен быть таким:

Status1   Status2  Dt1                  Dt2
A         D        05.23.2019 10:00:00  05.23.2019 13:00:00
A         D        05.23.2019 14:00:00  05.23.2019 17:00:00
A         null     05.23.2019 18:00:00  null

У меня есть мойСобственное решение, основанное на перекрестном / внешнем применении. С точки зрения производительности мне нужно решение без перекрестного / внешнего применения.

Ответы [ 2 ]

1 голос
/ 23 мая 2019

Поскольку столбцы результата Status1 и Status2 всегда кажутся "A" и "D" соответственно, я опускаю их в своем результате.

DECLARE @Data TABLE
(
    [Dt] SMALLDATETIME,
    [Status] CHAR(1)
);

INSERT INTO @Data ([Dt], [Status]) VALUES
    ('2019-05-23 10:00:00', 'A'),
    ('2019-05-23 11:00:00', 'B'),
    ('2019-05-23 12:00:00', 'B'),
    ('2019-05-23 13:00:00', 'D'),
    ('2019-05-23 14:00:00', 'A'),
    ('2019-05-23 15:00:00', 'B'),
    ('2019-05-23 16:00:00', 'C'),
    ('2019-05-23 17:00:00', 'D'),
    ('2019-05-23 18:00:00', 'A'),
    ('2019-05-23 19:00:00', 'D'),
    ('2019-05-23 20:00:00', 'D'),
    ('2019-05-23 21:00:00', 'A'),
    ('2019-05-23 22:00:00', 'A'),
    ('2019-05-23 23:00:00', 'A');

SELECT
    D.[Dt] AS [Dt1],
    [LastDBeforeNextA].[Dt] AS [Dt2]
FROM
    @Data AS D
    OUTER APPLY (SELECT TOP (1) [Dt]
                 FROM @Data
                 WHERE [Status] = 'A' AND [Dt] > D.[Dt]
                 ORDER BY [Dt]) AS [NextA]
    OUTER APPLY (SELECT TOP (1) [Dt]
                 FROM @Data
                 WHERE [Status] = 'D' AND [Dt] < [NextA].[Dt] AND [Dt] > D.[Dt]
                 ORDER BY [Dt] DESC) AS [LastDBeforeNextA]
WHERE
    D.[Status] = 'A' AND
    ([NextA].[Dt] > [LastDBeforeNextA].[Dt] OR ([LastDBeforeNextA].[Dt] IS NULL AND [NextA].[Dt] IS NULL))

Изначально он получает все записи из таблицыгде status равен 'A' (используется выражение D.[Status] = 'A' в предложении WHERE).

Для каждой найденной записи он объединяет дату следующей записи со статусом A (табличное выражение с псевдонимом NextA)и дата последней записи со статусом D, которая идет непосредственно перед следующей A-записью, но после текущей A-записи (табличное выражение с псевдонимом LastDBeforeNextA).

Результаты действительны, когда D-записьнайдено (выражение [NextA].[Dt] > [LastDBeforeNextA].[Dt] в предложении WHERE) или когда еще нет D-записи (выражение [LastDBeforeNextA].[Dt] IS NULL в предложении WHERE).Однако в последнем случае вам нужно получить последнюю A-запись (выражение [NextA].[Dt] IS NULL в предложении WHERE), поскольку после последней D-записи может быть несколько A-записей.

1 голос
/ 23 мая 2019

Мы можем попытаться использовать ROW_NUMBER здесь вместе с некоторой логикой поворота:

WITH cte AS (
    SELECT *,
        ROW_NUMBER() OVER (PARTITION BY Status ORDER BY Dt) rn
    FROM yourTable
    WHERE Status IN ('A', 'D')
)

SELECT
    MAX(CASE WHEN Status = 'A' THEN Status END) AS Status1,
    MAX(CASE WHEN Status = 'D' THEN Status END) AS Status2,
    MAX(CASE WHEN Status = 'A' THEN Dt END) AS Dt1,
    MAX(CASE WHEN Status = 'D' THEN Dt END) AS Dt2
FROM cte
GROUP BY rn
ORDER BY rn;

enter image description here

Демо

Идея состоит в том, чтобы сгенерировать последовательность номеров строк по всей таблице для каждого отдельного значения Status (A или D).Затем агрегируйте по этой последовательности номеров строк, чтобы объединить записи A и D.

...