2016-01-14 25 views
0

鑑於約會的桌子,就像這樣:SQL和時間數據

User  Start     End 
UserA 2016-01-15 12:00:00  2016-01-15 14:00:00  
UserA 2016-01-15 15:00:00  2016-01-15 17:00:00  
UserB 2016-01-15 13:00:00  2016-01-15 15:00:00  
UserB 2016-01-15 13:32:00  2016-01-15 15:00:00  
UserB 2016-01-15 15:30:00  2016-01-15 15:30:00  
UserB 2016-01-15 15:45:00  2016-01-15 16:00:00  
UserB 2016-01-15 17:30:00  2016-01-15 18:00:00  

我想創造不同的時間間隔的列表,其中的人是一樣的量有個約會:

Start     End     Count 
2016-01-15 12:00:00 2016-01-15 13:00:00 1 
2016-01-15 13:00:00 2016-01-15 14:00:00 2 
2016-01-15 14:00:00 2016-01-15 15:45:00 1 
2016-01-15 15:45:00 2016-01-15 16:00:00 2 
2016-01-15 16:00:00 2016-01-15 17:00:00 1 
2016-01-15 17:00:00 2016-01-15 17:30:00 0 
2016-01-15 17:30:00 2016-01-15 18:00:00 1 

如何在SQL中執行此操作,最好是SQL Server 2008?

編輯:爲了澄清:

Time 12 13 14 15 16 17 
UserA xxxxxxxx xxxxxxxx 
UserB  xxxxxxxx x  xx 
Count 1 2 1  21 0 1 

這一結果:手動,則結果是通過使一列針對每個用戶,標誌着阻塞時間,然後總結具有的標記行的計數得到設置將從可用的最短時間開始,在可用的最大時間結束,而ASCII藝術只有15分鐘的分辨率,我會要求至少分辨率。我想你可以在結果中留下「0」的行,如果這對你更容易。

+0

您真的需要'0'行嗎?基於零源行將行變爲存在可能會非常棘手。 –

+0

你從哪裏獲得期望結果集的開始和結束時間?它們與您的數據不匹配,並且不符合模式。 –

+0

@devlincarnate - 我認爲具有相同用戶數的相鄰時段會被整合,而且用戶似乎有相互重疊的約會。 –

回答

4

一定有什麼比這更簡單的方法,但至少你也許可以跟隨每一個步驟分別:

declare @t table ([User] varchar(19) not null,Start datetime2 not null,[End] datetime2 not null) 
insert into @t([User], Start, [End]) values 
('UserA','2016-01-15T12:00:00','2016-01-15T14:00:00'), 
('UserA','2016-01-15T15:00:00','2016-01-15T17:00:00'), 
('UserB','2016-01-15T13:00:00','2016-01-15T15:00:00'), 
('UserB','2016-01-15T13:32:00','2016-01-15T15:00:00'), 
('UserB','2016-01-15T15:30:00','2016-01-15T15:30:00'), 
('UserB','2016-01-15T15:45:00','2016-01-15T16:00:00'), 
('UserB','2016-01-15T17:30:00','2016-01-15T18:00:00') 

;With Times as (
    select Start as Point from @t 
    union 
    select [End] from @t 
), Ordered as (
    select Point,ROW_NUMBER() OVER (ORDER BY Point) as rn 
    from Times 
), Periods as (
    select 
     o1.Point as Start, 
     o2.Point as [End] 
    from 
     Ordered o1 
      inner join 
     Ordered o2 
      on 
       o1.rn = o2.rn - 1 
), UserCounts as (
select p.Start,p.[End],COUNT(distinct [User]) as Cnt,ROW_NUMBER() OVER (Order BY p.[Start]) as rn 
from 
    Periods p 
     left join 
    @t t 
     on 
      p.Start < t.[End] and 
      t.Start < p.[End] 
group by 
    p.Start,p.[End] 
), Consolidated as (
    select uc.* 
    from 
     UserCounts uc 
      left join 
     UserCounts uc_anti 
      on 
       uc.rn = uc_anti.rn + 1 and 
       uc.Cnt = uc_anti.Cnt 
    where 
     uc_anti.Cnt is null 
    union all 
    select c.Start,uc.[End],c.Cnt,uc.rn 
    from 
     Consolidated c 
      inner join 
     UserCounts uc 
      on 
       c.Cnt = uc.Cnt and 
       c.[End] = uc.Start 
) 
select 
    Start,MAX([End]) as [End],Cnt 
from 
    Consolidated 
group by 
    Start,Cnt 
order by Start 

CTE s爲 - Times - 因爲任何給定的開始或結束的郵票可以開始或結束在最後的結果中,我們只是將它們全部放在一列中 - 所以Ordered可以對它們進行編號,因此Periods可以將它們重新組裝到每個最小可能的時間段。

UserCounts然後返回到原始數據,並找出有多少用戶與每個計算週期重疊。

Consolidated是最棘手的CTE要遵循的,但它基本上是合併期間,彼此相鄰的用戶數相等。

結果:

Start      End       Cnt 
--------------------------- --------------------------- ----------- 
2016-01-15 12:00:00.0000000 2016-01-15 13:00:00.0000000 1 
2016-01-15 13:00:00.0000000 2016-01-15 14:00:00.0000000 2 
2016-01-15 14:00:00.0000000 2016-01-15 15:45:00.0000000 1 
2016-01-15 15:45:00.0000000 2016-01-15 16:00:00.0000000 2 
2016-01-15 16:00:00.0000000 2016-01-15 17:00:00.0000000 1 
2016-01-15 17:00:00.0000000 2016-01-15 17:30:00.0000000 0 
2016-01-15 17:30:00.0000000 2016-01-15 18:00:00.0000000 1 

(我甚至得到了零排我不確定我能想象到的存在)

0

這種查詢是容易得多,如果你寫一個calendar表。但在這個例子中,我使用recursive CTE在飛行中建立了一個。 CTE返回約會塊,然後我們可以加入約會數據。我無法確定樣本數據中的間隔模式,因此我已經以一小時爲單位顯示了結果。您可以修改此部分,或在第二個表格中定義您自己的部分。

樣本數據

/* Table variables make sharing data easier  
*/ 
DECLARE @Sample TABLE 
    (
     [User]  VARCHAR(50), 
     [Start]  DATETIME, 
     [End]  DATETIME 
    ) 
; 

INSERT INTO @Sample 
    (
     [User], 
     [Start], 
     [End] 
    ) 
VALUES 
    ('UserA', '2016-01-15 12:00:00', '2016-01-15 14:00:00'), 
    ('UserA', '2016-01-15 15:00:00', '2016-01-15 17:00:00'), 
    ('UserB', '2016-01-15 13:00:00', '2016-01-15 15:00:00'), 
    ('UserB', '2016-01-15 13:32:00', '2016-01-15 15:00:00'), 
    ('UserB', '2016-01-15 15:30:00', '2016-01-15 15:30:00'), 
    ('UserB', '2016-01-15 15:45:00', '2016-01-15 16:00:00'), 
    ('UserB', '2016-01-15 17:30:00', '2016-01-15 18:00:00') 
; 

我用兩個變量來限制返回的結果只是那些屬於給定的起點和終點內的約會。

/* Set an start and end point for the next query  
*/ 
DECLARE @Start DATETIME = '2016-01-15 12:00:00'; 
DECLARE @End DATETIME = '2016-01-15 18:00:00'; 

WITH Calendar AS 
    (
      /* Anchor returns start of first appointment  
      */ 
      SELECT 
       @Start           AS [Start], 
       DATEADD(SECOND, -1, DATEADD(HOUR, 1, @Start)) AS [End] 

     UNION ALL 

      /* Recursion, keep adding new records until end of last appointment  
      */ 
      SELECT 
       DATEADD(HOUR, 1, [Start]) AS [Start], 
       DATEADD(HOUR, 1, [End])  AS [End] 
      FROM 
       Calendar 
      WHERE 
       [End] <= @End 
    ) 
SELECT 
    c [Start], 
    c [End], 
    COUNT(DISTINCT s [User]) AS [Count] 
FROM 
    Calendar AS c 
     LEFT OUTER JOIN @Sample AS s   ON s [Start] BETWEEN c [Start] AND c [End] 
               OR s [End] BETWEEN c [Start] AND c [End] 
GROUP BY 
    c [Start], 
    c [End] 
; 

因爲約會可能會超過一個小時,它可能會導致多行。這解釋了爲什麼7個樣品行導致返回總數爲9.