さて、ここで説明が欠けているのは、次の両方を計算する TSQL です: - 1 時間ごとにアクティブなセッションの合計数と - 1 時間ごとにアクティブな同時セッションの最大数。
編集: 更新された質問のサンプル データが使用され、同時セッションを示す最後のクエリの出力にセッション ID が含まれるようになり、以前の最適化のバグが修正され、パフォーマンスが大幅に向上しました。
注意: これらのクエリは、SessionId
値が行ごとに異なる場合に最適に機能します。すべての行にの値を使用する1
と、残念な結果になります。したがって、列のIDENTITY
プロパティ。SessionId
-- Parameters.
declare @Start as DateTime = '20120901 00:00:00'
declare @End as DateTime = '20120901 12:00:00'
declare @Interval as Time = '01:00:00.00' -- One hour.
select @Start as [Start], @End as [End], @Interval as [Interval]
-- Sample data.
declare @Sessions as Table ( SessionId Int Identity, SessionStart DateTime, SessionEnd DateTime )
insert into @Sessions ( SessionStart, SessionEnd ) values
( '20120901 00:00:00', '20120901 05:59:59' ), -- Several hours in a single session.
( '20120901 01:01:00', '20120901 01:01:30' ), -- An assortment of overlapping ...
( '20120901 01:02:00', '20120901 01:03:30' ), -- ... sessions during a single hour.
( '20120901 00:00:05.077', '20120901 00:04:02.280' ),
( '20120901 00:00:14.687', '20120901 00:06:05.947' ),
( '20120901 00:00:17.857', '20120901 00:07:34.757' ),
( '20120901 00:00:25.843', '20120901 00:07:38.720' ),
( '20120901 00:00:29.427', '20120901 00:01:58.180' ),
( '20120901 00:00:31.853', '20120901 00:05:10.733' ),
( '20120901 00:00:40.693', '20120901 00:00:44.237' ),
( '20120901 00:00:58.773', '20120901 00:06:14.667' ),
( '20120901 00:00:59.457', '20120901 00:01:01.310' ),
( '20120901 00:01:16.390', '20120901 00:11:18.383' )
select * from @Sessions
-- Summary of sessions active at any time during each hour.
; with SampleWindows as (
select @Start as WindowStart, @Start + @Interval as WindowEnd
union all
select SW.WindowStart + @Interval, SW.WindowEnd + @Interval
from SampleWindows as SW
where SW.WindowEnd < @End
)
select SW.WindowStart, Count( S.SessionStart ) as [Sessions]
from SampleWindows as SW left outer join
@Sessions as S on SW.WindowStart <= S.SessionEnd and S.SessionStart < SW.WindowEnd
group by SW.WindowStart
-- Summary of maximum concurrent sessions active during each hour.
; with SampleWindows as (
select 1 as SampleWindowId, @Start as WindowStart, @Start + @Interval as WindowEnd
union all
select SW.SampleWindowId + 1, SW.WindowStart + @Interval, SW.WindowEnd + @Interval
from SampleWindows as SW
where SW.WindowEnd < @End
),
ActiveSessionsDuringWindow as (
select SW.SampleWindowId, SW.WindowStart, SW.WindowEnd, S.SessionId, S.SessionStart, S.SessionEnd,
-- A "pane" is the more restrictive of the window and the session start/end times.
case when SW.WindowStart <= S.SessionStart then S.SessionStart else SW.WindowStart end as PaneStart,
case when SW.WindowEnd >= S.SessionEnd then S.SessionEnd else SW.WindowEnd end as PaneEnd
from SampleWindows as SW left outer join
@Sessions as S on SW.WindowStart <= S.SessionEnd and S.SessionStart < SW.WindowEnd
),
ConcurrentSearch as (
select SampleWindowId, WindowStart, WindowEnd, SessionId, SessionStart, SessionEnd, PaneStart, PaneEnd,
Cast( '|' + Right( Replicate( '0', 3 ) + Cast( SessionId as VarChar(4) ), 4 ) + '|' as VarChar(1024) ) as SessionIds,
Cast( case when SessionId is NULL then 0 else 1 end as Int ) as Sessions
from ActiveSessionsDuringWindow
union all
select CS.SampleWindowId, CS.WindowStart, CS.WindowEnd, ASDW.SessionId, CS.SessionStart, CS.SessionEnd,
case when CS.PaneStart <= ASDW.PaneStart then ASDW.PaneStart else CS.PaneStart end as PaneStart,
case when CS.PaneEnd >= ASDW.PaneEnd then ASDW.PaneEnd else CS.PaneEnd end as PaneEnd,
Cast( CS.SessionIds + Right( Replicate( '0', 3 ) + Cast( ASDW.SessionId as VarChar(4) ), 4 ) + '|' as VarChar(1024) ),
CS.Sessions + 1
from ConcurrentSearch as CS inner join
ActiveSessionsDuringWindow as ASDW on ASDW.SampleWindowId = CS.SampleWindowId and
-- We haven't visited this session along this path.
CS.SessionId < ASDW.SessionId and -- EDIT: Reduce the size of the search tree.
CharIndex( '|' + Right( Replicate( '0', 3 ) + Cast( ASDW.SessionId as VarChar(4) ), 4 ) + '|', CS.SessionIds ) = 0 and
-- The session's pane overlaps the concurrent search pane.
CS.PaneStart <= ASDW.PaneEnd and ASDW.PaneStart <= CS.PaneEnd
)
select WindowStart, Max( Sessions ) as Sessions,
( select top 1 SessionIds from ConcurrentSearch where Sessions = Max( CS.Sessions ) ) as SessionIds
from ConcurrentSearch as CS
group by WindowStart
@Sessions
以下は、テーブルの行 ID 値を使用しない最後のクエリのバリエーションです。代わりにRow_Number()
、クエリの実行中に適切な値を割り当てるために使用します。これはまた、SessionId
値が 4 桁を超えないという前提を、任意の時間内にアクティブなセッションが 9,999 を超えないという前提に変更します。
-- Summary of maximum concurrent sessions active during each hour.
; with SampleWindows as (
select 1 as SampleWindowId, @Start as WindowStart, @Start + @Interval as WindowEnd
union all
select SW.SampleWindowId + 1, SW.WindowStart + @Interval, SW.WindowEnd + @Interval
from SampleWindows as SW
where SW.WindowEnd < @End
),
ActiveSessionsDuringWindow as (
select SW.SampleWindowId, SW.WindowStart, SW.WindowEnd, S.SessionStart, S.SessionEnd,
-- A "pane" is the more restrictive of the window and the session start/end times.
case when SW.WindowStart <= S.SessionStart then S.SessionStart else SW.WindowStart end as PaneStart,
case when SW.WindowEnd >= S.SessionEnd then S.SessionEnd else SW.WindowEnd end as PaneEnd,
Row_Number() over ( partition by SW.SampleWindowId order by S.SessionStart ) as SampleId
from SampleWindows as SW left outer join
@Sessions as S on SW.WindowStart <= S.SessionEnd and S.SessionStart < SW.WindowEnd
),
ConcurrentSearch as (
select SampleWindowId, WindowStart, WindowEnd, SampleId, SessionStart, SessionEnd, PaneStart, PaneEnd,
Cast( '|' + Right( Replicate( '0', 3 ) + Cast( SampleId as VarChar(4) ), 4 ) + '|' as VarChar(1024) ) as SampleIds,
Cast( case when SampleId is NULL then 0 else 1 end as Int ) as Sessions
from ActiveSessionsDuringWindow
union all
select CS.SampleWindowId, CS.WindowStart, CS.WindowEnd, ASDW.SampleId, CS.SessionStart, CS.SessionEnd,
case when CS.PaneStart <= ASDW.PaneStart then ASDW.PaneStart else CS.PaneStart end as PaneStart,
case when CS.PaneEnd >= ASDW.PaneEnd then ASDW.PaneEnd else CS.PaneEnd end as PaneEnd,
Cast( CS.SampleIds + Right( Replicate( '0', 3 ) + Cast( ASDW.SampleId as VarChar(4) ), 4 ) + '|' as VarChar(1024) ),
CS.Sessions + 1
from ConcurrentSearch as CS inner join
ActiveSessionsDuringWindow as ASDW on ASDW.SampleWindowId = CS.SampleWindowId and
-- We haven't visited this session along this path.
CS.SampleId < ASDW.SampleId and -- EDIT: Reduce the size of the search tree.
CharIndex( '|' + Right( Replicate( '0', 3 ) + Cast( ASDW.SampleId as VarChar(4) ), 4 ) + '|', CS.SampleIds ) = 0 and
-- The session's pane overlaps the concurrent search pane.
CS.PaneStart <= ASDW.PaneEnd and ASDW.PaneStart <= CS.PaneEnd
)
select WindowStart, Max( Sessions ) as Sessions
from ConcurrentSearch as CS
group by WindowStart
これは、既存のテーブルに対して実行するように簡単に変更できるはずです。SessionStart
昇順、昇順の単一インデックスにより、SessionEnd
パフォーマンスが向上するはずです。