SSRSで失われた時間のレポートを作成しようとしています。単一の日付範囲に必要なものを提供するスクリプトを見つけましたが、これは複数の日付に必要であり、範囲の合計が必要です。
したがって、6つの日付範囲がある場合、月/年の組み合わせとタイプの組み合わせ(LまたはR)の合計が必要です。
DECLARE @s SMALLDATETIME, @e SMALLDATETIME;
SELECT @s = '20161209', @e = '20170113';
;WITH n(n) AS
(
SELECT TOP (DATEDIFF(MONTH, @s, @e)+1) ROW_NUMBER() OVER
(ORDER BY [object_id])-1 FROM sys.all_objects
),
x(n,fd,ld) AS
(
SELECT n.n, DATEADD(MONTH, n.n, m.m), DATEADD(MONTH, n.n+1, m.m)
FROM n, (SELECT DATEADD(DAY, 1-DAY(@s), @s)) AS m(m)
)
SELECT [Month] = DATENAME(MONTH, fd), [Days] = DATEDIFF(DAY, fd, ld)
- CASE WHEN @s > fd THEN (DATEDIFF(DAY, fd, @s)+1) ELSE 0 END
- CASE WHEN @e < ld THEN (DATEDIFF(DAY, @e, ld)-1) ELSE 0 END
FROM x;
私のテーブルはHRAL
と呼ばれ、メインフィールドはBeginDate
(包括的)、EndDate
(排他的)およびType
です。
StartDate EndDate Type
2017-09-28 2017-10-02 L
2017-10-03 2017-10-10 R
2016-11-10 2016-11-11 L
2017-08-17 2017-12-25 R
Date Days Type
2017-09-01 3 L
2017-10-01 1 L
2017-10-01 38 R
2017-11-01 1 L
2017-08-01 15 R
2017-09-01 30 R
2017-11-01 30 R
2017-12-01 24 R
ソリューションには再帰が必要だと思いますか?
まず、月の開始日と終了日だけを格納する calendar table の非常に簡略化されたバージョンを作成します。
CREATE TABLE #LAZY_DATE_DIM (
MONTH_START_DATE DATETIME,
MONTH_END_DATE DATETIME,
PRIMARY KEY (MONTH_START_DATE)
);
INSERT INTO #LAZY_DATE_DIM WITH (TABLOCK)
SELECT DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '18991201')
, DATEADD(DAY, -1, DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '19000101' ))
FROM master..spt_values;
データベースに永続的なカレンダーテーブルを作成すると便利な場合がありますが、それができない場合は、オンザフライで必要なデータを生成する方法があります。
次のステップは、サンプルデータをモックアップすることです。
CREATE TABLE #HRAL (
StartDate DATETIME,
EndDate DATETIME,
[Type] VARCHAR(1)
);
INSERT INTO #HRAL VALUES ('2017-09-28','2017-10-02','L');
INSERT INTO #HRAL VALUES ('2017-10-03','2017-10-10','R');
INSERT INTO #HRAL VALUES ('2016-11-10','2016-11-11','L');
INSERT INTO #HRAL VALUES ('2017-08-17','2017-12-25','R');
クエリ全体をいくつかの異なるステップに分類しました。最初のステップは、HRAL
テーブルの開始日と終了日と交差するすべての関連する月の行を取得するようにテーブルを結合することです。私は次のコードを使用しました:
SELECT
h.*
, dd.*
FROM #HRAL h
INNER JOIN #LAZY_DATE_DIM dd ON
dd.MONTH_START_DATE > DATEADD(MONTH, -1, h.StartDate)
AND dd.MONTH_START_DATE < h.EndDate
中間結果:
╔═════════════════════════╦═════════════════════════╦══════╦═════════════════════════╦═════════════════════════╗
║ StartDate ║ EndDate ║ Type ║ MONTH_START_DATE ║ MONTH_END_DATE ║
╠═════════════════════════╬═════════════════════════╬══════╬═════════════════════════╬═════════════════════════╣
║ 2017-09-28 00:00:00.000 ║ 2017-10-02 00:00:00.000 ║ L ║ 2017-09-01 00:00:00.000 ║ 2017-09-30 00:00:00.000 ║
║ 2017-09-28 00:00:00.000 ║ 2017-10-02 00:00:00.000 ║ L ║ 2017-10-01 00:00:00.000 ║ 2017-10-31 00:00:00.000 ║
║ 2017-10-03 00:00:00.000 ║ 2017-10-10 00:00:00.000 ║ R ║ 2017-10-01 00:00:00.000 ║ 2017-10-31 00:00:00.000 ║
║ 2016-11-10 00:00:00.000 ║ 2016-11-11 00:00:00.000 ║ L ║ 2016-11-01 00:00:00.000 ║ 2016-11-30 00:00:00.000 ║
║ 2017-08-17 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ R ║ 2017-08-01 00:00:00.000 ║ 2017-08-31 00:00:00.000 ║
║ 2017-08-17 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ R ║ 2017-09-01 00:00:00.000 ║ 2017-09-30 00:00:00.000 ║
║ 2017-08-17 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ R ║ 2017-10-01 00:00:00.000 ║ 2017-10-31 00:00:00.000 ║
║ 2017-08-17 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ R ║ 2017-11-01 00:00:00.000 ║ 2017-11-30 00:00:00.000 ║
║ 2017-08-17 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ R ║ 2017-12-01 00:00:00.000 ║ 2017-12-31 00:00:00.000 ║
╚═════════════════════════╩═════════════════════════╩══════╩═════════════════════════╩═════════════════════════╝
2つの開始日の最大値、2つの終了日の最小値(カレンダーテーブルから月の終了日に1を加算)を取り、DATEDIFF
を使用して、日々。以下は、中間列の値の一部を示すクエリです。
SELECT
dd.MONTH_START_DATE
, h.StartDate
, start_dt.dt calc_start_dt
, h.[Type]
, dd.MONTH_END_DATE
, h.EndDate
, end_dt.dt calc_end_dt
FROM #HRAL h
INNER JOIN #LAZY_DATE_DIM dd ON
dd.MONTH_START_DATE > DATEADD(MONTH, -1, h.StartDate)
AND dd.MONTH_START_DATE < h.EndDate
CROSS APPLY (
SELECT MAX(start_dt)
FROM (VALUES (dd.MONTH_START_DATE), (h.StartDate)) x (start_dt)
) start_dt (dt)
CROSS APPLY (
SELECT MIN(end_dt)
FROM (VALUES (DATEADD(DAY, 1, dd.MONTH_END_DATE)), (h.EndDate)) x (end_dt)
) end_dt (dt)
結果セット:
╔═════════════════════════╦═════════════════════════╦═════════════════════════╦══════╦═════════════════════════╦═════════════════════════╦═════════════════════════╗
║ MONTH_START_DATE ║ StartDate ║ calc_start_dt ║ Type ║ MONTH_END_DATE ║ EndDate ║ calc_end_dt ║
╠═════════════════════════╬═════════════════════════╬═════════════════════════╬══════╬═════════════════════════╬═════════════════════════╬═════════════════════════╣
║ 2017-09-01 00:00:00.000 ║ 2017-09-28 00:00:00.000 ║ 2017-09-28 00:00:00.000 ║ L ║ 2017-09-30 00:00:00.000 ║ 2017-10-02 00:00:00.000 ║ 2017-10-01 00:00:00.000 ║
║ 2017-10-01 00:00:00.000 ║ 2017-09-28 00:00:00.000 ║ 2017-10-01 00:00:00.000 ║ L ║ 2017-10-31 00:00:00.000 ║ 2017-10-02 00:00:00.000 ║ 2017-10-02 00:00:00.000 ║
║ 2017-10-01 00:00:00.000 ║ 2017-10-03 00:00:00.000 ║ 2017-10-03 00:00:00.000 ║ R ║ 2017-10-31 00:00:00.000 ║ 2017-10-10 00:00:00.000 ║ 2017-10-10 00:00:00.000 ║
║ 2016-11-01 00:00:00.000 ║ 2016-11-10 00:00:00.000 ║ 2016-11-10 00:00:00.000 ║ L ║ 2016-11-30 00:00:00.000 ║ 2016-11-11 00:00:00.000 ║ 2016-11-11 00:00:00.000 ║
║ 2017-08-01 00:00:00.000 ║ 2017-08-17 00:00:00.000 ║ 2017-08-17 00:00:00.000 ║ R ║ 2017-08-31 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ 2017-09-01 00:00:00.000 ║
║ 2017-09-01 00:00:00.000 ║ 2017-08-17 00:00:00.000 ║ 2017-09-01 00:00:00.000 ║ R ║ 2017-09-30 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ 2017-10-01 00:00:00.000 ║
║ 2017-10-01 00:00:00.000 ║ 2017-08-17 00:00:00.000 ║ 2017-10-01 00:00:00.000 ║ R ║ 2017-10-31 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ 2017-11-01 00:00:00.000 ║
║ 2017-11-01 00:00:00.000 ║ 2017-08-17 00:00:00.000 ║ 2017-11-01 00:00:00.000 ║ R ║ 2017-11-30 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ 2017-12-01 00:00:00.000 ║
║ 2017-12-01 00:00:00.000 ║ 2017-08-17 00:00:00.000 ║ 2017-12-01 00:00:00.000 ║ R ║ 2017-12-31 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║ 2017-12-25 00:00:00.000 ║
╚═════════════════════════╩═════════════════════════╩═════════════════════════╩══════╩═════════════════════════╩═════════════════════════╩═════════════════════════╝
これで、DATEDIFF
とGROUP BY
を併用して、すべてをまとめることができます。最後のクエリ:
SELECT
dd.MONTH_START_DATE
, SUM(DATEDIFF(DAY, start_dt.dt, end_dt.dt)) [days]
, h.[Type]
FROM #HRAL h
INNER JOIN #LAZY_DATE_DIM dd ON
dd.MONTH_START_DATE > DATEADD(MONTH, -1, h.StartDate)
AND dd.MONTH_START_DATE < h.EndDate
CROSS APPLY (
SELECT MAX(start_dt)
FROM (VALUES (dd.MONTH_START_DATE), (h.StartDate)) x (start_dt)
) start_dt (dt)
CROSS APPLY (
SELECT MIN(end_dt)
FROM (VALUES (DATEADD(DAY, 1, dd.MONTH_END_DATE)), (h.EndDate)) x (end_dt)
) end_dt (dt)
GROUP BY
dd.MONTH_START_DATE
, h.[Type];
結果はあなたのものと一致します:
╔═════════════════════════╦══════╦══════╗
║ MONTH_START_DATE ║ days ║ Type ║
╠═════════════════════════╬══════╬══════╣
║ 2016-11-01 00:00:00.000 ║ 1 ║ L ║
║ 2017-09-01 00:00:00.000 ║ 3 ║ L ║
║ 2017-10-01 00:00:00.000 ║ 1 ║ L ║
║ 2017-08-01 00:00:00.000 ║ 15 ║ R ║
║ 2017-09-01 00:00:00.000 ║ 30 ║ R ║
║ 2017-10-01 00:00:00.000 ║ 38 ║ R ║
║ 2017-11-01 00:00:00.000 ║ 30 ║ R ║
║ 2017-12-01 00:00:00.000 ║ 24 ║ R ║
╚═════════════════════════╩══════╩══════╝
CROSS APPLY
構文を避けたい場合は、CASE
式を使用して同じことを実行できます。
遅くなりませんように。
Lemmeは、他のサンプルデータで機能しないかどうかを知っています。他のサンプルデータに対して誤った出力が出た場合は修正できると思います。
FOM(毎月1日)のみを格納する通常のカレンダーテーブルがあります
CREATE TABLE tblcalender (
DATEVal DATETIME
PRIMARY KEY (DATEVal)
);
INSERT INTO tblcalender WITH (TABLOCK)
SELECT DATEADD(MONTH, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '18991201')
FROM master..spt_values;
サンプルデータ、
CREATE TABLE #TMP(StartDate DATE, EndDate DATE,Type1 CHAR(1))
INSERT INTO #TMP VALUES
('2017-09-28','2017-10-02','L')
,('2017-10-03','2017-10-10','R')
,('2016-11-10','2016-11-11','L')
,('2017-08-17','2017-12-25','R')
--SELECT * FROM #TMP
ために Sql server 2008
以下、
;WITH CTE
AS (
SELECT DATEVal
,StartDate
,EndDate
,Type1
,CASE
WHEN year(StartDate) = year(EndDate)
AND month(StartDate) = month(EndDate)
THEN '1'
WHEN month(DATEVal) = month(StartDate)
AND year(DATEVal) = year(ca.StartDate)
THEN 'S'
WHEN DATEVal > StartDate
AND DATEVal < dateadd(month, - 1, DATEADD(month, datediff(month, 0, ca.EndDate) + 1, 0))
THEN 'W'
ELSE 'E'
END flag
FROM tblcalender
CROSS APPLY (
SELECT StartDate
,EndDate
,Type1
FROM #tmp
WHERE DATEVal >= dateadd(month, - 1, DATEADD(month, datediff(month, 0, StartDate) + 1, 0))
AND DATEVal <= dateadd(day, - 1, DATEADD(month, datediff(month, 0, EndDate) + 1, 0))
) ca
)
,CTE1
AS (
SELECT DATEVal
,CASE
WHEN flag = '1'
THEN datediff(day, StartDate, EndDate)
WHEN flag = 'S'
THEN datediff(day, StartDate, dateadd(day, - 1, DATEADD(month, datediff(month, 0, StartDate) + 1, 0))) + 1
WHEN flag = 'E'
THEN datediff(day, dateadd(month, - 1, DATEADD(month, datediff(month, 0, endDate) + 1, 0)), endDate)
ELSE day(dateadd(day, - 1, DATEADD(month, datediff(month, 0, DATEVal) + 1, 0))) --W
END [Days]
,Type1
,StartDate
,EndDate
,Flag
FROM CTE
)
--select * from CTE1
SELECT DATEVal
,Type1
,SUM([Days]) [Days]
FROM CTE1
GROUP BY DATEVal
,Type1
DROP TABLE #TMP
ために Sql server 2012
以上、わずかな変更。EOMonth and FORMAT
コードを短くします。
;with CTE as
(
select DATEVal,StartDate,EndDate,Type1
,case when FORMAT(StartDate,'MMyyyy')=FORMAT(EndDate,'MMyyyy')
THEN '1'
when FORMAT(StartDate,'MMyyyy')=FORMAT(DATEVal,'MMyyyy')
then 'S'
when DATEVal>StartDate and DATEVal< dateadd(day,1,EOMONTH(EndDate,-1))--FOM
THEN 'W'
else 'E' END flag
from tblcalender
cross APPLY(
select StartDate,EndDate,Type1 from #tmp
where DATEVal>= dateadd(day,1,EOMONTH(StartDate,-1))
and
DATEVal<=EOMONTH(EndDate)
)ca
)
,CTE1 as
(
select DATEVal
, case when flag='1'
THEN datediff(day,StartDate,EndDate)
when flag='S'
THEN datediff(day,StartDate,EOMONTH(StartDate))+1
when flag='E'
THEN datediff(day,dateadd(day,1,EOMONTH(endDate,-1)),endDate)
else day(EOMONTH(DATEVal))--W
end [Days]
,Type1,StartDate,EndDate,Flag
from CTE
)
--select * from CTE1
SELECT DATEVal,Type1
,SUM([Days])[Days]
from CTE1
group by DATEVal,Type1
DROP TABLE #TMP