根据间隔的15分钟计算

时间:2015-11-17 07:27:44

标签: sql-server date count

我正在使用SQL Server 2008 R2,我有一个表:

tbl_calls

cl_Id
cl_StartDate
cl_endDate

我将两个参数@StartDate@EndDate传递给我的存储过程。

我的要求是在每15分钟的持续时间之间计算记录数

示例:

@StartDate = '2015-11-16 00:00:00.000', 
@EndDate = '2015-11-16 23:59:00.000'

输出应为:

Date                        Count
2015-11-16 00:00:00.000      10(Count of startDate between '2015-11-16 00:00:00.000' AND '2015-11-16 00:15:00.000')
2015-11-16 00:15:00.000       7(Count of startDate between '2015-11-16 00:15:00.000' AND '2015-11-16 00:30:00.000')
2015-11-16 00:30:00.000      50(Count of startDate between '2015-11-16 00:30:00.000' AND '2015-11-16 00:45:00.000')

upto @EndDate

我试图这样做,但没有想到,我不确定下面的查询是否在逻辑附近。

我尝试的是:

DECLARE @StartDate DATETIME = DATEADD(DAY,-1,GETUTCDATE()),
        @EndDate DATETIME = GETUTCDATE()

SELECT New 
FROM
    (SELECT 
         (CASE 
             WHEN cl_StartTime BETWEEN @StartDate AND DATEADD(MINUTE, 15, @StartDate) 
                THEN 1 
                ELSE 0 
          END) AS New 
     FROM          
         tbl_Calls WITH (NOLOCK)    
     WHERE 
         cl_StartTime BETWEEN @StartDate AND @EndDate) AS Inners 
GROUP BY 
    New

如果您需要更多详细信息,请与我们联系。

谢谢。

4 个答案:

答案 0 :(得分:3)

首先,您需要生成从@StartDate@EndDate的所有15分钟间隔。您可以在Tally Table.的帮助下执行此操作,然后在LEFT JOIN上执行tbl_calls来计算通话次数:

SQL Fiddle

DECLARE @StartDate  DATETIME = '2015-11-16 00:00:00.000',
        @EndDate    DATETIME = '2015-11-16 23:59:00.000'

DECLARE @nRows INT

SELECT @nRows = DATEDIFF(MINUTE, @StartDate, DATEADD(DAY, 1, CAST(@EndDate AS DATE))) / 15

;WITH E1(N) AS(
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b),
Tally(N) AS(
    SELECT TOP(@nRows)
        ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
    FROM E8
),
Intervals(sd, ed) AS(
    SELECT
        DATEADD(MINUTE, (t.N - 1) * 15, @StartDate),
        DATEADD(MINUTE, N * 15, @StartDate)
    FROM Tally t
)
SELECT 
    i.sd, i.ed, cnt = COUNT(c.cl_Id)
FROM Intervals i
LEFT JOIN tbl_calls c
    ON i.sd <= c.cl_EndDate
    AND i.ed > c.cl_Startdate
GROUP BY i.sd, i.ed

请注意,此处生成的时间间隔为@StartDate,直至@EndDate + 1天的开头,即从'2015-11-16 00:00:00.000''2015-11-17 00:00:00.000'

此外,JOIN条件指定呼叫持续时间必须在间隔开始和间隔结束之间,但间隔结束不得与呼叫重叠。持续时间。您可以看到此answer以获得更多解释。

您可以根据自己的喜好修改JOIN条件,但这基本上就是要点。

答案 1 :(得分:0)

一种方法是创建一个包含所需插槽的表(请参阅下面的#slots)....

declare @start_date datetime = '2015-11-17'

select dateadd( minute, rn, '2015-11-17' ) as start_date
into #src
from (
    select row_number() over( order by object_id ) - 1 as rn
    from sys.columns
    ) as rn;

create index cl on #src ( start_date );

select dateadd( minute, 15 * rn, @start_date ) as slot_start
into #slots
from (
    select row_number() over( order by object_id ) - 1 as rn
    from sys.columns
    ) as rn;

select s.slot_start, count(*) as rows_in_slot
from #slots s
    inner join #src d on d.start_date >= s.slot_start 
                     and d.start_date < dateadd( minute, 15, s.slot_start )
group by s.slot_start
order by s.slot_start

答案 2 :(得分:0)

此方法生成一个日期列表,从开始日期开始,到结束日期结束,相隔15分钟(TableA)。它计算tbl_calls中落在15分钟内的所有记录并给出计数。

declare @startdate datetime; set @StartDate = '2015-11-16 00:00:00.000';
declare @EndDate datetime; set @EndDate = '2015-11-16 23:59:00.000';

WITH TableA (DateSlotStart, DateSlotEnd) AS (
    SELECT  dateadd(n, (number * 15), @StartDate) AS DateSlotStart  
          , dateadd(n, (number + 1) * 15, @StartDate) AS DateSlotEnd
    FROM master.dbo.spt_values 
    WHERE name IS NULL and dateadd(n, (number) * 15, @StartDate) <= @EndDate
)
select DateSlotStart, DateSlotEnd, count(cl_Id) from tableA
left join @tbl_calls calls on calls.cl_StartDate >= DateSlotStart and calls.cl_StartDate <  DateSlotEnd
group by DateSlotStart, DateSlotEnd

答案 3 :(得分:0)

和其他人一样,我建议创建一个保存间隔的表格。这将为您提供加入和GROUP BY所需的记录。

在我的示例中,我使用recursive CTE创建了一个间隔表。但是还有很多其他方法可以实现这一点,包括使用真正的物理表。我还在另一个CTE中创建了一些样本记录,所以任何人都可以运行这个例子。

如果你想查找没有打开票证的间隔,你可以使用OUTER JOIN重做这个例子。

Example

DECLARE @StartTime    SMALLDATETIME = '2015-01-01 09:00:00.000'; 
DECLARE @EndTime    SMALLDATETIME = '2015-01-01 10:30:00.000';

WITH tbl_Calls AS
    (
        /* This CTE returns some sample records to help 
         * illistrate the princple.
         * Each sample record has a start and end date 
         * between 9:00 and 10:30 on Jan 1st 2015.
         */
        SELECT
            r.*
        FROM
            (
                VALUES
                    (1, '2015-01-01 09:00:00.000', '2015-01-01 09:30:00.000'),
                    (2, '2015-01-01 09:01:00.000', '2015-01-01 10:00:00.000'),
                    (3, '2015-01-01 09:02:00.000', '2015-01-01 10:15:00.000'),
                    (4, '2015-01-01 09:03:00.000', '2015-01-01 09:14:00.000'),
                    (5, '2015-01-01 09:15:00.000', '2015-01-01 09:16:00.000' ),
                    (6, '2015-01-01 09:16:00.000', '2015-01-01 10:30:00.000')
            ) AS r (cl_Id, cl_StartDate, cl_EndDate)
    ),
    Interval AS
    (
        /* This CTE returns 1 record for each 15 min interval
         * between the start and end time.
         * PLEASE NOTE: This CTE uses recursion to generate the 
         * required records.
         */
            SELECT
                @StartTime                            AS IntervalStart,
                DATEADD(MINUTE, 15, @StartTime)        AS IntervalEnd

        UNION ALL

            SELECT
                -- Addding a millisecond prevents overlapping intervals.
                DATEADD(MILLISECOND, 1, IntervalEnd)    AS IntervalStart,        
                DATEADD(MINUTE, 15, IntervalEnd)        AS IntervalEnd
            FROM
                Interval
            WHERE
                IntervalEnd <= @EndTime
    )
-- ******************************************************
-- The CTEs above this section exist only to provide
-- sample records.
-- The technique for banding date times is shown below.
-- ******************************************************
SELECT        
    i.IntervalStart,
    COUNT(c.cl_Id)        AS RecordsOpened
FROM
    tbl_Calls AS c
        INNER JOIN Interval AS i        ON    c.cl_StartDate >=    i.IntervalStart 
                                        AND c.cl_StartDate <    i.IntervalEnd
GROUP BY
    i.IntervalStart        
;