Как написать CTE в SQL Server, чтобы получить дату из предыдущей строки для человека с типом = NA.
Если имеется несколько предыдущих строк с NA, при сортировке по возрастанию dt_eff выберите первую.
Если между началом записи и типом = NA есть какие-либо другие типы, то эти записи следует игнорировать. Рассматривайте другие типы для того же человека только после того, как предыдущий тип закончился (сценарий «Лица 124»).
Источник данных
Person Type dt_eff
123 A 2018-10-23 <Start of record >
123 NA 2018-11-19 <Should be the end date for above and dont select this in output>
123 NA 2018-12-25 <dont select this in output>
124 A 2020-01-01 <Start of record >
124 B 2020-02-15 <Ignore and dont select in output>
124 NA 2020-05-14 <Should be the end date for start of record and dont select in op>
124 C 2020-10-13 <As the above start record has ended this should be new start>
124 NA 2021-01-15 <should be the end date for second start record>
124 A 2021-05-22 <As the above start record has ended this should be new start>
124 T 2021-08-22 <Ignored and dont select in output>
456 NA 2022-04-19 <Ignore as there is no lag record with valid type>
456 A 2022-05-01 <Start of record and null as end date as there is no type = NA>
456 B 2022-07-15 <Ignore>
Ожидаемый результат
Person Type dt_start dt_end
123 A 2018-10-23 2018-11-19
124 A 2020-01-01 2020-05-14
124 C 2020-10-13 2021-01-15
124 A 2021-05-22 NULL
456 A 2022-05-01 NULL
DML и DDL для вышеуказанных исходных данных
CREATE TABLE Person (
Person INTEGER,
Type VARCHAR(3),
dt_eff Date
);
INSERT INTO Person (Person, Type, dt_eff)
VALUES
(123,'A','2018-10-23'),
(123,'NA','2018-11-19'),
(123,'NA','2018-12-25'),
(124,'A','2020-01-01'),
(124,'B','2020-02-15'),
(124,'NA','2020-05-14'),
(124,'C','2020-10-13'),
(124,'NA','2021-01-15'),
(124,'A','2021-05-22'),
(124,'T','2021-08-22'),
(456,'NA','2022-04-19'),
(456,'A','2022-05-01'),
(456,'B','2022-07-15')
Пытаться
with cte1 as (
select *
, lead(dt_eff) over (partition by Person order by dt_eff) dt_eff_lead
, lag(Type, 1, Type) over (partition by Person order by dt_eff) type_lag
from Person
), cte2 as (
select Person, Type, dt_eff Start_Date
, dt_eff_lead
, sum(case when Type <> type_lag and type='NA' then 1 else 0 end)
over (partition by person order by dt_eff asc
rows between unbounded preceding and current row) TypeGroup
from cte1
)
select Person, Type, Start_Date as dt_start
, max(dt_eff_lead) over (partition by Person, TypeGroup) dt_end
from cte2
where Type<>'NA'
order by Person, Start_Date, Type;
Добавлен запрос. нужен столбец короля флагов в cte2, где написано игнорировать эту запись + dt_end должен быть нулевым для начальной записи, когда нет предыдущей записи с типом = NA
Рекомендую присмотреться к гэпам и островам
Вы можете выполнить группировку на основе общего количества строк NA:
SELECT Person, Type
, dt_start, CASE WHEN cntNA > 0 THEN dt_end END AS dt_end
FROM (
SELECT MIN(dt_eff) OVER(PARTITION BY person, grouping) AS dt_start
, MAX(dt_eff) OVER(PARTITION BY person, grouping) AS dt_end
, COUNT(CASE WHEN type = 'NA' THEN 1 END) OVER(PARTITION BY person, grouping) AS cntNA
, ROW_NUMBER() OVER(PARTITION BY person, grouping ORDER BY dt_eff) AS startrow
, *
FROM (
SELECT *
, COUNT(CASE WHEN Type = 'NA' THEN 1 END) OVER(PARTITION BY Person ORDER BY dt_eff ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS grouping
FROM
(
VALUES (123, N'A', N'2018-10-23')
, (123, N'NA', N'2018-11-19')
, (123, N'NA', N'2018-12-25')
, (124, N'A', N'2020-01-01')
, (124, N'B', N'2020-02-15')
, (124, N'NA', N'2020-05-14')
, (124, N'C', N'2020-10-13')
, (124, N'NA', N'2021-01-15')
, (124, N'A', N'2021-05-22')
, (124, N'T', N'2021-08-22')
, (456, N'NA', N'2022-04-19')
, (456, N'A', N'2022-05-01')
, (456, N'B', N'2022-07-15')
) t (Person,Type,dt_eff)
) x
) x
WHERE startrow = 1
AND type <> 'NA'
COUNT(CASE WHEN Type = 'NA' THEN 1 END) OVER(PARTITION BY Person ORDER BY dt_eff ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
создает указанное выше значение группировки. AND 1 PRECEDING
означает, что первая NA засчитывается по отношению к предыдущим строкам, и это то, что вам нужно для группировки.
Затем вы создаете минимальную/максимальную дату для каждой группы, а также определяете, какая строка является первой, поскольку вас интересует только одна строка на группу. cntNA
содержит количество NA в группе, так как вам нужно обнулить dt_end для тех, у кого нет NA.
Наконец, вы выбираете то, что ищете. CASE WHEN cntNA > 0 THEN dt_end END
создает открытые даты
Ух ты. Подпадает ли эта группа под «разрыв и остров»? кто-то посоветовал мне изучить пробел и группировку островов. Для меня ваш запрос выглядит как разрыв и группировка островов. Изучение новых вещей. Это работает так, как и ожидалось, и очень эффективно. Буду тестировать на больших данных. Спасибо.
Да, я бы сказал, что это метод пробелов и островов, нужно каким-то образом подсчитывать желаемые значения и создавать беговую группу, вариаций на эту тему, конечно, тоже много. Это очень полезно во многих ситуациях
Спасибо. очень полезно. вместе с cte начну искать пробелы и остров.
Включен дополнительный фильтр (type='NA') в запрос, который вы указали во вчерашнем сообщении. sum (случай, когда Type <> type_lag и Type='NA', затем 1, иначе 0 end)». Это дает дату окончания предыдущей записи, где type='NA'. Но не уверен, насколько эффективно мы можем фильтровать записи и заканчивать дата равна нулю, если нет предыдущей записи с типом = NA