Возможен рекурсивный запрос CTE с использованием диапазонов дат - PullRequest
4 голосов
/ 10 августа 2011

Не уверен, как даже сформулировать заголовок на этом!

У меня есть следующие данные:

IF OBJECT_ID ('tempdb..#data') IS NOT NULL DROP TABLE #data
CREATE TABLE #data
(
id UNIQUEIDENTIFIER
,reference NVARCHAR(30)
,start_date DATETIME
,end_date DATETIME
,lapse_date DATETIME
,value_received DECIMAL(18,3)
)

INSERT INTO #data VALUES ('BE91B9C1-C02F-46F7-9B63-4D0B25D9BA2F','168780','2006-05-01 00:00:00.000',NULL,'2011-09-27 00:00:00.000',537.42)
INSERT INTO #data VALUES ('B538F123-C839-447A-B300-5D16EACF4560','320858','2011-08-08 00:00:00.000',NULL,NULL,0)
INSERT INTO #data VALUES ('1922465D-2A55-434D-BAAA-8E15D681CF12','306597','2011-04-08 00:00:00.000','2011-06-22 13:14:40.083','2011-08-07 00:00:00.000',12)
INSERT INTO #data VALUES ('7DF8FBCC-B490-4892-BDC5-8FD2D73B0323','321461','2011-07-01 00:00:00.000',NULL,'2011-09-25 00:00:00.000',8.44)
INSERT INTO #data VALUES ('1EC2E754-F325-4313-BDFC-9010E255F6FE','74215','2000-10-31 00:00:00.000',NULL,'2011-08-30 00:00:00.000',258)
INSERT INTO #data VALUES ('9E59B09C-0198-48AC-8EEC-A0D76CEA9385','169194','2008-06-25 00:00:00.000',NULL,'2011-09-25 00:00:00.000',1766.4)
INSERT INTO #data VALUES ('97CF6C0F-324A-49A6-B9D8-AC848A1F821A','288039','2010-09-01 00:00:00.000','2011-07-29 00:00:00.000','2011-08-21 00:00:00.000',55)
INSERT INTO #data VALUES ('97CF6C0F-324A-49A6-B9D8-AC848A1F821A','324423','2011-08-01 00:00:00.000',NULL,'2011-09-25 00:00:00.000',5)
INSERT INTO #data VALUES ('D5E5197A-E8E1-468C-9991-C8712224C2BF','323395','2011-08-25 00:00:00.000',NULL,NULL,0)
INSERT INTO #data VALUES ('0EC4976C-16B9-4C99-BD07-D0CBDF014D32','323741','2011-08-25 00:00:00.000',NULL,NULL,0)

И я хочу иметь возможность сгруппировать все ссылки в категории «активные», «истекшие» или «новые» на основе следующих критериев:

  • Active имеет начальную дату, которая меньше последней даты базового месяца, дату истечения после последнего дня предыдущего месяца и value_received> 0;

  • New имеет дату начала, которая попадает в базовый месяц;

  • Lapsed имеет дату истечения срока, которая попадает в отчетный месяц.

И затем применить эти определения для каждой ссылки в течение скользящих 13 месяцев (то есть начиная с Начиная с июля 2010 года), чтобы за каждый месяц я мог видеть, сколько ссылок попадает в каждую группу.

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

select 
id
,reference
,start_date
,end_date
,lapse_date 
,value_received
,CASE   WHEN start_date < DATEADD(month,DATEPART(Month,GETDATE()) + 1,DATEADD(year,DATEPART(year,GETDATE())-1900,0)) --next month start date
        AND lapse_date > DATEADD(ms,-3,DATEADD(mm,DATEDIFF(mm,0,GETDATE())+1,0)) --last day of current month
        AND value_received > 0
        THEN 'Active'
        WHEN lapse_date < DATEADD(month,DATEPART(Month,GETDATE()) + 1,DATEADD(year,DATEPART(year,GETDATE())-1900,0)) --next month start
            AND lapse_date > DATEADD(ms,-3,DATEADD(mm,DATEDIFF(mm,0,GETDATE()),0)) --last day of prior month
        THEN 'lapse'
        WHEN start_date < DATEADD(month,DATEPART(Month,GETDATE()) + 1,DATEADD(year,DATEPART(year,GETDATE())-1900,0)) --next month start date
        AND start_date > DATEADD(ms,-3,DATEADD(mm,DATEDIFF(mm,0,GETDATE()),0)) --last day of prior month
        THEN 'New'
        ELSE 'Not applicable'
 END AS [type]
from #data

Но я не вижу хорошего / эффективного способа сделать это (кроме как повторить этот запрос 13 раз и объединить результаты, которые, я знаю, просто ужасны)

Будет ли это случай использования текущего месяца в качестве якоря и использования рекурсии (если да, то некоторые указатели будут наиболее полезны)?

Любая помощь наиболее ценится как всегда:)

* Отредактировано для включения фактического решения *

В случае, если это кому-нибудь интересно, это последний запрос, который я использовал:

;WITH Months as 
(
SELECT DATEADD(ms,-3,DATEADD(mm,DATEDIFF(mm,0,GETDATE())+1,0)) as month_end
,0 AS level
UNION ALL     
SELECT DATEADD(month, -1, month_end)as month_end
,level + 1 FROM Months
WHERE level < 13 
) 
SELECT 
DATENAME(Month,month_end) + ' ' + DATENAME(YEAR,month_end) as date
,SUM(CASE WHEN start_date <= month_end
        AND Month(start_date) <> MONTH(Month_end)
        AND lapse_date > Month_end 
 THEN 1 ELSE 0 END) AS Active
,SUM(CASE WHEN start_date <= Month_end 
        AND DATENAME(MONTH,start_date) + ' ' + DATENAME(YEAR,start_date) = 
        DATENAME(MONTH,month_end) + ' ' + DATENAME(YEAR,month_end)
THEN 1 ELSE 0 END) AS New
,SUM(CASE WHEN lapse_date <= Month_end 
        AND Month(lapse_date) = MONTH(Month_end)
THEN 1 ELSE 0 END) AS lapse
FROM #data
CROSS JOIN Months
WHERE id IS NOT NULL
AND start_date IS NOT NULL
GROUP BY DATENAME(Month,month_end)  + ' ' + DATENAME(YEAR,month_end) 
ORDER by MAX(level) ASC

Ответы [ 3 ]

5 голосов
/ 10 августа 2011

Здесь вам не нужен "реальный" рекурсивный CTE. Вы можете использовать один для ссылок на месяц, хотя:

;WITH Months
as
(
    SELECT DATEADD(day, -DATEPART(day, GETDATE())+1, GETDATE()) as 'MonthStart'
    UNION ALL
    SELECT DATEADD(month, -1, MonthStart) as 'MonthStart'
    FROM Months
)

Тогда вы можете JOIN до SELECT TOP 13 * FROM Months в вашем запросе.

Я не собираюсь пытаться проанализировать все ваши CASE заявления, но по сути вы можете использовать GROUP BY в поле даты и MonthStart поля, например:

GROUP BY Datepart(year, monthstart), Datepart(month, monthstart)

и агрегировать по месяцам. Вероятно, будет проще всего иметь все ваши параметры (активные, пропущенные и т. Д.) В виде столбцов и вычислять каждый из них с SUM(CASE WHEN ... THEN 1 ELSE 0 END), поскольку это будет легче с GROUP BY.

2 голосов
/ 10 августа 2011

Вы можете объединить ваш запрос с рекурсивным CTE, это хорошая идея.

WITH thirteenMonthBack(myDate, level) as
(
   SELECT GETDATE() as myDate, 0 as level
   UNION ALL
   SELECT DATEADD(month, -1, myDate), level + 1
   FROM thirteenMonthBack
   WHERE level < 13
)
SELECT xxx
FROM youQuery
   CROSS JOIN thirteenMonthBack
0 голосов
/ 12 марта 2018
DECLARE @date DATE = GETDATE()

;WITH MonthsCTE AS (
    SELECT 1 [Month], DATEADD(DAY, -DATEPART(DAY, @date)+1, @date) as 'MonthStart'
    UNION ALL
    SELECT [Month] + 1, DATEADD(MONTH, 1, MonthStart)
    FROM MonthsCTE 
    WHERE [Month] < 12 )

SELECT * FROM MonthsCTE
...