0

出于某种原因,我看到使用此查询的时间间隔存在差距。仅使用基本数据时,我就可以使用它。但是,当加入我的表并指定 WHERE 子句时,我看到我的时间间隔有间隙。我还需要将 S.SessionEndTime 合并到我的间隔中,以查找与 ResponseTime 和 SessionEndTime 之间的给定 1 分钟间隔重叠的记录计数。

这是我正在使用的查询。通过使用派生表,我在 1 分钟间隔内基于 COUNT 获得每小时 MAX。

    DECLARE @start_date     DATETIME
DECLARE @end_date       DATETIME
DECLARE @interval TIME

SET @start_date = '08/01/2012'
SET @end_date = '08/01/2012 12:00:00'
SET @interval = '00:01:00.00';

    WITH TimeWindowsCTE AS 
    ( 
        SELECT 
            @start_date AS WindowStart, @start_date + @interval AS WindowEnd 
        UNION ALL 
        SELECT 
            SW.WindowStart + @Interval, SW.WindowEnd + @interval 
        FROM 
            TimeWindowsCTE AS SW 
        WHERE 
            SW.WindowEnd < @end_date 
    ) 
    SELECT 
        DATEADD(hour, DATEDIFF(hour, CONVERT(datetime, '19000101', 112), t1.WindowStart), CONVERT(datetime, '19000101', 112)) WindowStart, 
        MAX([Sessions]) [Sessions]
    FROM 
    (
        SELECT 
            SW.WindowStart, COUNT(*) [Sessions] 
        FROM 
            TimeWindowsCTE AS SW 
            LEFT OUTER JOIN SessionDetails AS S ON SW.WindowStart <= S.ResponseTime and S.ResponseTime < SW.WindowEnd
            --I need to introduce the S.SessionEndTime into the JOIN so I can count the number of records that 
            --fall within the interval
            LEFT OUTER JOIN VoipDetails V on S.SessionIdTime = V.SessionIdTime and S.SessionIdSeq = V.SessionIdSeq                      
            WHERE V.ToGatewayId IS NOT NULL
        GROUP BY 
            SW.WindowStart
    ) t1
    GROUP BY 
        DATEADD(hour, DATEDIFF(hour, CONVERT(datetime, '19000101', 112), t1.WindowStart), CONVERT(datetime, '19000101', 112))
    ORDER BY 
        DATEADD(hour, DATEDIFF(hour, CONVERT(datetime, '19000101', 112), t1.WindowStart), CONVERT(datetime, '19000101', 112))
    OPTION (maxrecursion 0)

这是我得到的输出

日期会议
2012-08-01 00:00:00.000 5
2012-08-01 01:00:00.000 3
2012-08-01 02:00:00.000 2
2012-08-01 03:00:00.000 2
2012-08-01 05:00:00.000 1
2012-08-01 06:00:00.000 2
2012-08-01 07:00:00.000 3
2012-08-01 08:00:00.000 2
2012-08-01 09:00:00.000 2
2012-08-01 10:00:00.000 1

在 04:00:00 期间我需要一个 0。我想要完成的是确定在该给定小时内发生了多少并发会话,而不是在一小时的顶部有多少会话。这就是重叠发挥作用的地方。如果在上午 8 点 56 分开始通话并在上午 9 点 26 分结束,则应该在上午 8 点钟至少有 1 个通话,在上午 9 点钟至少有 1 个通话。然后根据一天中每小时的并发会话数,我找到给定日期的 MAX,然后在我的报告中使用它。

这是数据的基本结构:

插入@Sessions ( SessionIdSeq, ResponseTime, SessionEndTime ) 值
    (1, '20120901 00:00:00', '20120901 05:59:59' ), -- 单个会话中的几个小时。
    (1, '20120901 01:01:00', '20120901 01:01:30' ), -- 各种重叠...
    (1, '20120901 01:02:00', '20120901 01:03:30' ), -- ... 一个小时内的会话。
    (1, '20120901 00:00:05.077', '20120901 00:04:02.280'),
    (1, '20120901 00:00:14.687', '20120901 00:06:05.947'),
    (1, '20120901 00:00:17.857', '20120901 00:07:34.757'),
    (1, '20120901 00:00:25.843', '20120901 00:07:38.720'),
    (1, '20120901 00:00:29.427', '20120901 00:01:58.180'),
    (1, '20120901 00:00:31.853', '20120901 00:05:10.733'),
    (1, '20120901 00:00:40.693', '20120901 00:00:44.237'),
    (1, '20120901 00:00:58.773', '20120901 00:06:14.667'),
    (1, '20120901 00:00:59.457', '20120901 00:01:01.310'),
    (1, '20120901 00:01:16.390', '20120901 00:11:18.383')
4

1 回答 1

0

Okay, lacking a clarification here is some TSQL that computes both: - the total number of sessions active in each hour and - the maximum number of concurrent sessions active in each hour.

EDIT: The sample data from the updated question has been used, the output of the last query which shows concurrent sessions now includes the session ids, and a bug in the prior optimization was corrected which greatly improves performance.

NB: These queries work best when the SessionId values vary from row to row. Using a value of 1 for all rows will cause disappointing results. Hence the IDENTITY property on the SessionId column.

-- 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 

Following is a variation on the last query which does not use row id values from the @Sessions table. Instead it uses Row_Number() to assign suitable values for the duration of the query. This also changes the assumption that the SessionId values do not exceed four digits to an assumption that there are not more than 9,999 sessions active within any given hour.

-- 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  

This should be easy to modify to run against an existing table. A single index on SessionStart ascending, SessionEnd ascending should improve performance.

于 2012-09-19T23:31:48.883 回答