私はそれをいくつかのステップに分割します(それぞれに個別のCTEが与えられます):
declare @Ranges table (FromDate date not null,ToDate date not null)
insert into @Ranges (FromDate,ToDate) values
('20121110','20121115'),
('20121121','20121122'),
('20121130','20121201')
;with Months as (
select
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010101') as MonthStart,
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010131') as MonthEnd
from @Ranges
union /* not all */
select
DATEADD(month,DATEDIFF(month,'20010101',ToDate),'20010101') as MonthStart,
DATEADD(month,DATEDIFF(month,'20010101',ToDate),'20010131') as MonthEnd
from @Ranges
), MonthRanges as (
select
CASE WHEN r.FromDate > m.MonthStart then r.FromDate ELSE m.MonthStart END as StartRange,
CASE WHEN r.ToDate < m.MonthEnd then r.ToDate ELSE m.MonthEnd END as EndRange
from
@Ranges r
inner join
Months m
on
r.ToDate >= m.MonthStart and
r.FromDate <= m.MonthEnd
)
select
DATEPART(month,StartRange),
SUM(DATEDIFF(day,StartRange,EndRange)+1) /* Inclusive */
from
MonthRanges
group by
DATEPART(month,StartRange)
まず、Months
CTE は、関心のある各月の最初と最後の日を見つけます (*)。次に、MonthRanges
このデータを元の範囲と再結合し、必要に応じて分割して、扱っている各期間が 1 か月の日数のみを表すようにします。次にDATEDIFF
、各範囲にまたがる日数を計算するために使用できます (日付を扱っており、包括的な値が必要なため、1 を追加します)。
(*) Months
CTE は、複数の月にまたがる範囲を扱っておらず、その間の月に開始または終了する他の範囲がない場合に機能します。この状況に対処する必要がある場合は、Months
CTE を修正する必要があります。たとえば('20120115','20120315')
、上記のサンプルに (他の範囲を追加せずに) 追加すると、上記を使用して 2 月の結果は得られません。この状況に対処する必要がありますか?
(*) で示されている状況に対処するためMonths
に、上記のクエリの CTE を次のように置き換えることができます。
;With LastMonth as (
select MAX(ToDate) as Mx from @Ranges
), MultiMonths as (
select
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010101') as MonthStart,
DATEADD(month,DATEDIFF(month,'20010101',FromDate),'20010131') as MonthEnd
from @Ranges
union all
select
DATEADD(month,1,MonthStart),
DATEADD(month,1,MonthEnd)
from MultiMonths
where MonthStart <= (select Mx from LastMonth)
), Months as (
select distinct MonthStart,MonthEnd from MultiMonths
)