У меня есть следующий набор данных, в котором у человека есть идентификатор, и его количество может меняться со временем, поэтому изменения записываются как диапазоны дат. Иногда в диапазонах дат есть пробелы, в которых нет доступной информации. Это хорошо. Однако я хочу объединить непрерывные диапазоны дат, где они имеют одинаковое количество, в то время как оно охватывает несколько записей, таких как строки 2 и 3, для идентификатора = 1.
DECLARE @DataTable TABLE (
ID [int] NULL,
StartDate [date] NULL,
EndDate [date] NULL,
Amount [decimal](12,2) NULL
)
INSERT INTO @DataTable
SELECT 1, '20180101','20180513', 10.00 UNION ALL
SELECT 1, '20180630','20190301', 15.00 UNION ALL
SELECT 1, '20190302','20190615', 15.00 UNION ALL
SELECT 1, '20190616','20991231', 5.00 UNION ALL
SELECT 2, '20190101','20190331', 35.00 UNION ALL
SELECT 2, '20190401','20191031', 30.00 UNION ALL
SELECT 3, '20180505','20180930', 19.00 UNION ALL
SELECT 3, '20181001','20190228', 1.00 UNION ALL
SELECT 3, '20190501','20190815', 1.00 UNION ALL
SELECT 3, '20190819','20190827', 5.00 UNION ALL
SELECT 3, '20190828','20991231', 1.00 UNION ALL
SELECT 4, '2017-10-01', '2017-12-31', 688.96 UNION ALL
SELECT 4, '2018-01-01', '2018-04-30', 707.96 UNION ALL
SELECT 4, '2018-05-01', '2018-05-31', 783.96 UNION ALL
SELECT 4, '2018-06-01', '2018-12-31', 707.96 UNION ALL
SELECT 4, '2019-01-01', '2019-03-31', 707.96 UNION ALL
SELECT 4, '2019-04-01', '2019-04-30', 571.46 UNION ALL
SELECT 4, '2019-05-01', '2019-06-30', 707.96 UNION ALL
SELECT 4, '2019-07-01', '2099-12-31', 707.96
;
Я решил эту проблему, сгенерировав строки дат между начальной и конечной датами, используя dimDate, а затем вел запись, в которой либо сумма изменилась по сравнению с предыдущей записью, либо был пробел дат для идентификатора. Затем я использовал следующую доступную дату записи, чтобы использовать ее в качестве даты окончания. Запрос выглядит следующим образом:
WITH DateList AS (
SELECT DT.*, DD.DateOnly AS RecordDate
FROM @DataTable DT
INNER JOIN dimDate DD ON DT.StartDate <= DD.DateOnly AND CASE WHEN DT.EndDate > GETDATE() THEN CONVERT(DATE,GETDATE()) ELSE DT.EndDate END >= DD.DateOnly
)
, PrevValue AS (
SELECT
*
, LAG(RecordDate) OVER (PARTITION BY ID ORDER BY RecordDate) AS PrevDate
, LAG(Amount) OVER (PARTITION BY ID ORDER BY RecordDate) AS PrevAmt
FROM DateList
)
, KeepHistory AS (
SELECT
*
FROM PrevValue
WHERE Amount <> PrevAmt OR PrevAmt IS NULL OR DATEADD(DAY,1,PrevDate) <> RecordDate OR PrevDate IS NULL
)
, FINAL AS (
SELECT
*
, LEAD(PrevDate) OVER (PARTITION BY ID ORDER BY StartDate) AS NextEndDate
FROM KeepHistory
)
SELECT
ID
, StartDate
, CASE WHEN NextEndDate > EndDate THEN NextEndDate ELSE EndDate END AS EndDate
, Amount
FROM FINAL
Мой вопрос, есть ли альтернативный способ подойти к этому без прохождения dimDate или генерации дат между начальной и конечной датой. Можно ли этого добиться, просто используя оконные функции, такие как разрыв и проблема островов, например здесь ?
Пожалуйста, дайте мне знать, если у вас возникнут проблемы с моим текущим решением. Спасибо.
Что касается ответа @Larnu, ваш обновленный запрос работает для большинства. Я добавил пример ID = 4, который, кажется, вызывает проблему при объединении 4-й и 5-й строк. Он также объединяет 7-й ряд, когда 6-й ряд имеет разное количество.
ID = 4 4-е, 5-е и 7-е объединены;6-й имеет различное количество