2011-08-10 17 views
4

これでタイトルをどのようにフレーズするかわからない!日付範囲を使用した再帰的なCTEクエリ

私は、次のデータがあります。

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は、基準月内の経過日を有する。

し、各月の私は、各グループに分類どのように多くの参照を参照することができるように(今まで2010年7月のように戻ってからそう)ローリング13ヶ月ごとの参照用にこれらの定義を適用します。

私は現在の月のためにこれを定義するには、以下を使用することができる午前:このクエリを13回繰り返すこと以外(

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 

しかし、私はこれを行うための素敵な/効率的な方法を見ることができないと私が知っている結果はちょうどひどいです)

これは、現在の月をアンカーとして使用し、再帰を使用する場合です(もしそうなら、いくつかのポインタが最も高く評価されるでしょう)?

最もいつものように感謝すべてのヘルプ:)

*は

*実際のソリューションが含まれるように編集された場合には、それは誰にも関心のだ、これは私が使用し、最終的なクエリです:

;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 

答えて

4

「実」再帰CTEは必要ありません。あなたはしかし、月の参照のための1つを使用することができます。

;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文を解析しようとするつもりはないんだけど、基本的に、あなたは次のように、日付とMonthStartフィールド上GROUP BYを使用することができます。

月ごと

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

と集計。おそらく、すべてのオプション(アクティブ、失効など)を列として持ち、SUM(CASE WHEN ... THEN 1 ELSE 0 END)でそれぞれを計算するのが最も簡単です(GROUP BYで簡単になるでしょう)。

+0

このためのおかげで - 正しい方向に私を指摘し、別の列を使用することは、長期:) – Dibstar

+0

@Davinでより多くの意味を行います - 助けて幸せ。 – JNK

2

あなたのリクエストを再帰的な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

ありがとう、私はそれよりもむしろCTEの間にレベルをフィルタリングの概念が好きだった:) – Dibstar