将工作状态分为整个15分钟间隔或最近分钟

时间:2017-04-25 08:43:55

标签: sql sql-server group-by

我有一些数据需要按AgentID,工作状态和15分钟间隔或最近分钟进行分组,如果工作状态在15分钟的间隔内发生变化。

当我的代理从一个工作状态转到另一个工作状态时,会创建数据,如下所示:

DateTime                | AgentID | Workstate
---------------------------------------------------------------
2017-03-01 09:55:00     | RAR     | Customer Service
---------------------------------------------------------------
2017-03-01 10:18:00     | RAR     | Retention
---------------------------------------------------------------
2017-03-01 10:30:00     | RAR     | Customer Service
---------------------------------------------------------------
2017-03-01 10:45:00     | RAR     | Mail
---------------------------------------------------------------
2017-03-01 11:00:00     | RAR     | Customer Service
---------------------------------------------------------------
2017-03-01 11:53:00     | RAR     | Logged out
---------------------------------------------------------------

这些数据应按15分钟间隔分组,或者如果状态在15分钟间隔内变化,请使用最近的分钟,如下所示:

DateTime Start      | DateTime End        | AgentID | Workstate
-------------------------------------------------------------------------
2017-03-01 09:55:00 | 2017-03-01 10:00:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 10:00:00 | 2017-03-01 10:15:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 10:15:00 | 2017-03-01 10:18:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 10:18:00 | 2017-03-01 10:30:00 | RAR     | Retention
-------------------------------------------------------------------------
2017-03-01 10:30:00 | 2017-03-01 10:45:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 10:45:00 | 2017-03-01 11:00:00 | RAR     | Mail
-------------------------------------------------------------------------
2017-03-01 11:00:00 | 2017-03-01 11:15:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 11:15:00 | 2017-03-01 11:30:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 11:30:00 | 2017-03-01 11:45:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 11:45:00 | 2017-03-01 11:53:00 | RAR     | Customer Service
-------------------------------------------------------------------------
2017-03-01 11:53:00 | 2017-03-01 12:00:00 | RAR     | Logged out
-------------------------------------------------------------------------

我希望上面的描述有意义。

4 个答案:

答案 0 :(得分:1)

试试这个,按预期显示准确的结果。

    DECLARE @StartTime  VARCHAR(50)
DECLARE @EndTime    VARCHAR(50)
DECLARE @MM         VARCHAR(5)
DECLARE @StartTimeBucket    VARCHAR(50)
DECLARE @ENDTimeBucket      VARCHAR(50)
DECLARE @Cnt        INT
CREATE TABLE #Bucket (ID INT,StrtTime DATETIME,EndTime DATETIME,Rec_Cnt INT)
Create table #SampleData1 
(
   [Datetime] datetime,
   AgentID varchar(10),
   Workstate varchar(500),
   Row_Num  Int
)
INSERT INTO #SampleData1 (AgentID,Workstate,[Datetime],Row_Num)
Select AgentId,Workstate,[Datetime],
        ROW_NUMBER() OVER( Partition by AgentId order by Workstate)     
From SampleData
SET @StartTime = (SELECT MIN([Datetime]) FROM SampleData)
SET @EndTime = (SELECT MAX([Datetime]) FROM SampleData)
SET @MM = (SELECT SUBSTRING(@StartTime,16,2))
SET @StartTimeBucket = (SELECT CASE WHEN @MM BETWEEN 1 AND 15 THEN DATEADD(MINUTE,(15-@MM),@StartTime) WHEN @MM BETWEEN 15 AND 30 THEN DATEADD(MINUTE,(30-@MM),@StartTime) WHEN @MM BETWEEN 30 AND 45 THEN DATEADD(MINUTE,(45-@MM),@StartTime) WHEN @MM BETWEEN 45 AND 60 THEN DATEADD(MINUTE,(60-@MM),@StartTime) END)
SET @MM = (SELECT SUBSTRING(@EndTime,16,2))
SET @EndTimeBucket = (SELECT CASE WHEN @MM BETWEEN 1 AND 15 THEN DATEADD(MINUTE,(15-@MM),@EndTime) WHEN @MM BETWEEN 15 AND 30 THEN DATEADD(MINUTE,(30-@MM),@EndTime) WHEN @MM BETWEEN 30 AND 45 THEN DATEADD(MINUTE,(45-@MM),@EndTime) WHEN @MM BETWEEN 45 AND 60 THEN DATEADD(MINUTE,(60-@MM),@EndTime) END)
SET @Cnt = (SELECT DATEDIFF(MINUTE,@StartTimeBucket,@EndTimeBucket)/15 +1)
WHILE (@Cnt > 0)
BEGIN
INSERT INTO #Bucket (ID,StrtTime,EndTime,Rec_Cnt)
VALUES (@Cnt,DATEADD(Minute,-15,@EndTimeBucket),@EndTimeBucket,(SELECT COUNT(1) FROM SampleData WHERE [Datetime] BETWEEN DATEADD(Minute,-15,@EndTimeBucket) AND @EndTimeBucket))
SET @Cnt = @Cnt - 1
SET @EndTimeBucket = DATEADD(Minute,-15,@EndTimeBucket)
END

Select *
FROM
(
Select (CASE WHEN [DateTime] IS NOT NULL THEN [DateTime] ELSE StrtTime END) AS [DateTime Start],
        EndTime AS [DateTime End],--AgentID,id,LAG (AgentID) OVER (ORDER BY ID),LAG (AgentID,2) OVER (ORDER BY ID),
        (CASE WHEN AgentID IS NULL AND LAG (AgentID) OVER (ORDER BY ID) IS NULL THEN LAG (AgentID,2) OVER (ORDER BY ID) WHEN AgentID IS NULL THEN LAG (AgentID) OVER (ORDER BY ID) ELSE AgentID END) AS AgentID,
        (CASE WHEN Workstate IS NULL AND LAG (Workstate,1) OVER (ORDER BY ID) IS NULL THEN LAG (Workstate,2) OVER (ORDER BY ID) WHEN Workstate IS NULL THEN LAG (Workstate,1) OVER (ORDER BY ID) ELSE Workstate END) AS  Workstate
FROM (
    select * 
    from #SampleData1 INNER join #Bucket 
    ON [Datetime] >= StrtTime and [Datetime] <= EndTime
    UNION 
    SELECT NULL As [DateTime],NULL As AgentID,NULL As Workstate, Null As Row_Num,*
    FROM #Bucket WHERE Rec_Cnt = 0
    ) Final
WHERE ISNULL([DateTime],'') <> EndTime 

UNION
Select StrtTime AS [DateTime Start],
        [DateTime] AS [DateTime End],
        LAG (AgentID,1,NULL) OVER (ORDER BY ID) AS AgentID,
        LAG (Workstate,1,NULL) OVER (ORDER BY ID) AS  Workstate
FROM (
    select * 
    from #SampleData1 INNER join #Bucket 
    ON [Datetime] >= StrtTime and [Datetime] <= EndTime
    --order by Id
    UNION 
    SELECT NULL As [DateTime],NULL As AgentID,NULL As Workstate, Null As Row_Num,*
    FROM #Bucket WHERE Rec_Cnt = 0
    ) Final
WHERE StrtTime < [Datetime] and ISNULL([DateTime],'') <> EndTime 
) Final_Result
WHERE AgentID IS NOT NULL

答案 1 :(得分:0)

您可以像这样使用recursive CTECROSS APPLY

DECLARE @SampleData AS TABLE
(
   [Datetime] datetime,
   AgentID varchar(10),
   Workstate varchar(500)
)

INSERT INTO @SampleData
VALUES
('2017-03-01 09:55:00','RAR','Customer Service'),
('2017-03-01 10:18:00','RAR','Retention'),
('2017-03-01 10:30:00','RAR','Customer Service'),
('2017-03-01 10:45:00','RAR','Mail'),
('2017-03-01 11:00:00','RAR','Customer Service'),
('2017-03-01 11:53:00','RAR','Logged out')

DECLARE @MinDate datetime =  (SELECT min(sd.[Datetime]) FROM @SampleData sd)
DECLARE @MaxDate datetime =  (SELECT max(sd.[Datetime]) FROM @SampleData sd)

;WITH temp AS
(
   SELECT CAST('2017-03-01 00:00:00' AS datetime)  AS IntervalDate 

   UNION ALL

   SELECT dateadd(minute,15, t.IntervalDate) AS IntervalDate 
   FROM temp t
   WHERE t.IntervalDate < @MaxDate
)
SELECT 
      CASE WHEN sd.Datetime < cr.IntervalDate THEN sd.Datetime ELSE cr.IntervalDate END AS [Datetime Start],
      CASE WHEN sd.Datetime < cr.IntervalDate THEN cr.IntervalDate ELSE sd.Datetime END AS [Datetime End],
      sd.AgentID, 
      sd.Workstate
FROM @SampleData sd
CROSS APPLY
(
   SELECT * 
   FROM temp t
   WHERE      t.IntervalDate >= @MinDate
         AND (  
                datediff(minute, t.IntervalDate, sd.Datetime) BETWEEN 0 AND 15
                OR datediff(minute, sd.Datetime, t.IntervalDate) BETWEEN 0 AND 15
            )

) cr
OPTION (MAXRECURSION 0)

演示链接:Rextester

答案 2 :(得分:0)

绝对是一个难以解决的问题,但我找到了解决它的查询。它确实给你的输出。

一点点解释,我使用了两个常用的表表达式。在第一个中,我计算所有时间点的下一个和上一个时间间隔。接下来,我按照starttimepoint对它们进行分组并获取最小的端点。然后使用外部应用我填写agentid和工作状态的空值

DECLARE @t TABLE (Dt DATETIME, AgentId VARCHAR(255), WorkState VARCHAR(255))
INSERT INTO @t VALUES
('2017-03-01 09:55:00','RAR','Customer Service'),
('2017-03-01 10:18:00','RAR','Retention'),
('2017-03-01 10:30:00','RAR','Customer Service'),
('2017-03-01 10:45:00','RAR','Mail'),
('2017-03-01 11:00:00','RAR','Customer Service'),
('2017-03-01 11:53:00','RAR','Logged out')

DECLARE @d TABLE (DateTimes DATETIME)
INSERT INTO @d VALUES
('2017-03-01 10:00:00'),
('2017-03-01 10:15:00'),
('2017-03-01 10:30:00'),
('2017-03-01 10:45:00'),
('2017-03-01 11:00:00'),
('2017-03-01 11:15:00'),
('2017-03-01 11:30:00'),
('2017-03-01 11:45:00'),
('2017-03-01 12:00:00'),
('2017-03-01 12:15:00'),
('2017-03-01 12:30:00')

;With mCte AS (
SELECT t.Dt, CASE WHEN DATEPART(MINUTE, t.Dt) = 0 OR DATEPART(MINUTE, t.Dt) = 15 OR DATEPART(MINUTE, t.Dt) = 30 OR DATEPART(MINUTE, t.Dt) = 45 
              THEN CASE WHEN DATEPART(MINUTE, t.Dt) = 0 THEN DATEADD(MINUTE,15, t.Dt)
                        WHEN DATEPART(MINUTE, t.Dt) = 15 THEN DATEADD(MINUTE,15, t.Dt)
                        WHEN DATEPART(MINUTE, t.Dt) = 30 THEN DATEADD(MINUTE,15, t.Dt)
                        WHEN DATEPART(MINUTE, t.Dt) = 45 THEN DATEADD(MINUTE,15, t.Dt)
                    END
              ELSE CASE WHEN ABS(DATEDIFF(MINUTE, DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dt) + 1, 0), t.dt)) < 15 
                        THEN DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dt) + 1, 0)
                  WHEN ABS(DATEDIFF(MINUTE, DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dt) + 1, 0), t.dt)) < 30
                        THEN DATEADD(MINUTE, 45 - DATEPART(MINUTE,t.DT), t.DT)
                  WHEN ABS(DATEDIFF(MINUTE, DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dt) + 1, 0), t.dt)) < 45
                        THEN DATEADD(MINUTE, 30 - DATEPART(MINUTE,t.DT), t.DT)
                  WHEN ABS(DATEDIFF(MINUTE, DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dt) + 1, 0), t.dt)) < 60
                        THEN DATEADD(MINUTE, 15 - DATEPART(MINUTE,t.DT), t.DT)
            END
        END AS Interval, t.AgentId, t.WorkState
FROM    @t AS t 

UNION ALL

SELECT  CASE WHEN ISNULL(ABS(DATEDIFF(MINUTE, t.DT, LAG(t.Dt) OVER (ORDER BY t.DT))), 0) = 0
        THEN CASE WHEN DATEPART(MINUTE, t.Dt) > 45 THEN DATEADD(MINUTE, 45 - DATEPART(MINUTE,t.DT), t.DT)
                WHEN DATEPART(MINUTE, t.Dt) > 30 THEN DATEADD(MINUTE, 30 - DATEPART(MINUTE,t.DT), t.DT)
                WHEN DATEPART(MINUTE, t.Dt) > 15 THEN DATEADD(MINUTE, 15 - DATEPART(MINUTE,t.DT), t.DT)
                WHEN DATEPART(MINUTE, t.Dt) > 0 THEN DATEADD(MINUTE, 0 - DATEPART(MINUTE,t.DT), t.DT)
        END
        ELSE CASE
         WHEN ISNULL(ABS(DATEDIFF(MINUTE, t.DT, LAG(t.Dt) OVER (ORDER BY t.DT))), 0) > 45 
        THEN DATEADD(MINUTE, 45 - DATEPART(MINUTE,t.DT), t.DT)
         WHEN ISNULL(ABS(DATEDIFF(MINUTE, t.DT, LAG(t.Dt) OVER (ORDER BY t.DT))), 0) > 30 
        THEN DATEADD(MINUTE, 30 - DATEPART(MINUTE,t.DT), t.DT)
         WHEN ISNULL(ABS(DATEDIFF(MINUTE, t.DT, LAG(t.Dt) OVER (ORDER BY t.DT))), 0) > 15 
        THEN DATEADD(MINUTE, 15 - DATEPART(MINUTE,t.DT), t.DT)
        ELSE DATEADD(MINUTE,  -15 , t.Dt)
        END
    END AS Interval, t.Dt, LAG(t.AgentId) OVER(ORDER BY Dt) AS AgentId, LAG(t.WorkState) OVER(ORDER BY Dt) AS WorkState

FROM    @t AS t

UNION ALL

SELECT  d.DateTimes, DATEADD(MINUTE, 15, d.DateTimes) AS NextInterval, NULL, NULL
FROM    @d AS d 
), rmCte AS (
SELECT Dt, MIN(Interval) AS EndTime, MIN(AgentId) AS AgentId, MIN(WorkState) AS WorkState
FROM mCte
GROUP BY Dt)

SELECT f.Dt, f.EndTime, ISNULL(f.AgentId, f2.AgentId) AS AgentId, ISNULL(f.WorkState, f2.WorkState) AS WorkState
FROM rmCte AS f OUTER APPLY
(
    SELECT TOP 1 f2.*
    FROM rmCte AS f2
    WHERE f2.Dt >= f.Dt AND f2.EndTime <= f2.EndTime AND AgentId IS NOT NULL
) f2

结果

StartTime                EndTime                  AgentId  WorkState
2017-03-01 09:45:00.000  2017-03-01 09:55:00.000  RAR      Customer Service
2017-03-01 09:55:00.000  2017-03-01 10:00:00.000  RAR      Customer Service
2017-03-01 10:00:00.000  2017-03-01 10:15:00.000  RAR      Customer Service
2017-03-01 10:15:00.000  2017-03-01 10:18:00.000  RAR      Customer Service
2017-03-01 10:18:00.000  2017-03-01 10:30:00.000  RAR      Retention
2017-03-01 10:30:00.000  2017-03-01 10:45:00.000  RAR      Customer Service
2017-03-01 10:45:00.000  2017-03-01 11:00:00.000  RAR      Mail
2017-03-01 11:00:00.000  2017-03-01 11:15:00.000  RAR      Customer Service
2017-03-01 11:15:00.000  2017-03-01 11:30:00.000  RAR      Customer Service
2017-03-01 11:30:00.000  2017-03-01 11:45:00.000  RAR      Customer Service
2017-03-01 11:45:00.000  2017-03-01 11:53:00.000  RAR      Customer Service
2017-03-01 11:53:00.000  2017-03-01 12:00:00.000  RAR      Logged out
2017-03-01 12:00:00.000  2017-03-01 12:15:00.000  NULL     NULL
2017-03-01 12:15:00.000  2017-03-01 12:30:00.000  NULL     NULL
2017-03-01 12:30:00.000  2017-03-01 12:45:00.000  NULL     NULL

答案 3 :(得分:0)

请尝试这个:

Create table #SampleData 
(
   [Datetime] datetime,
   AgentID varchar(10),
   Workstate varchar(500)
)

INSERT INTO #SampleData
VALUES ('2017-03-01 09:55:00','RAR','Customer Service'), ('2017-03-01 10:18:00','RAR','Retention'),
('2017-03-01 10:30:00','RAR','Customer Service'), ('2017-03-01 10:45:00','RAR','Mail'),
('2017-03-01 11:00:00','RAR','Customer Service'), ('2017-03-01 11:53:00','RAR','Logged out')

declare @startTrns datetime =  convert(datetime,convert(date, (select min ([datetime]) from #SampleData)))
declare @endTrns datetime =  convert(datetime,(select max ([datetime]) from #SampleData))

select [datetime], AgentID, Workstate, case when [next_datetime] is null then 
            case when datepart(minute, [datetime]) <= 15 then dateadd(minute, (15 - datepart(minute, [datetime])), [datetime])
                 when datepart(minute, [datetime]) <= 30 then dateadd(minute, (30 - datepart(minute, [datetime])), [datetime])
                 when datepart(minute, [datetime]) <= 45 then dateadd(minute, (45 - datepart(minute, [datetime])), [datetime])
                 when datepart(minute, [datetime]) <= 60 then dateadd(minute, (60 - datepart(minute, [datetime])), [datetime])
            end
         else [next_datetime] end as [next_datetime]
INTO #SampleData1
from (
    select [datetime], LEAD([datetime],1,null) over (order by [datetime]) as [next_datetime], 
    AgentID, Workstate
    from #SampleData
)a

;with time_interval as (
    select @startTrns as [timeinterval]
    union all
    select dateadd(minute, 15, [timeinterval]) from time_interval
    where [timeinterval] < @endTrns
)

select [timeinterval], isnull(LEAD([timeinterval],1,null) over (order by [timeinterval]), dateadd(minute, 15, [timeinterval])) as [next_timeinterval]
into #time_intvl
from time_interval


select AgentID, Workstate, [datetime], [timeinterval], [next_timeinterval], [next_datetime], row_number() over (order by [datetime]) as rnk
into #SampleData2
from (
    select sd1.AgentID, sd1.Workstate, sd1.[datetime], sd1.[next_datetime], tim.[timeinterval], tim. [next_timeinterval]
    from #SampleData1 sd1
    inner join #time_intvl tim on tim.[timeinterval] between sd1.[datetime] and sd1.[next_datetime]
    union all
    select sd2.AgentID, sd2.Workstate, sd2.[datetime], sd2.[next_datetime], tim2.[timeinterval], tim2.[next_timeinterval]
    from #SampleData1 sd2
    inner join #time_intvl tim2 on tim2.[timeinterval] < sd2.[next_datetime] and sd2.[next_datetime] >= tim2.[next_timeinterval]
            and sd2.[datetime] < tim2.[timeinterval]
)b

select distinct [DateTime Start], [DateTime End], AgentID, Workstate 
from (
    select AgentID, Workstate,
           case when rnk <> 1 then 
                case when [timeinterval] < [next_datetime] then [timeinterval] else [datetime] end
           else [datetime] end as [DateTime Start],
           case when rnk <> 1 then 
                case when [next_datetime] < [next_timeinterval] then [next_datetime] else [next_timeinterval] end
           else [timeinterval] end as [DateTime End]
    from #SampleData2
)temp
order by [DateTime Start]

drop table #SampleData
drop table #SampleData1
drop table #SampleData2
drop table #time_intvl

enter image description here