按日期不完全连续的连续日期分组记录

时间:2017-01-18 13:52:17

标签: sql sql-server tsql date group-by

我有一些包含日期的数据。我试图按连续日期对数据进行分组,但是,日期并不是完全连续的。这是一个例子:

DateColumn              | Value
------------------------+-------
2017-01-18 01:12:34.107 | 215426 <- batch no. 1
2017-01-18 01:12:34.113 | 215636
2017-01-18 01:12:34.623 | 123516
2017-01-18 01:12:34.633 | 289926
2017-01-18 04:58:42.660 | 259063 <- batch no. 2
2017-01-18 04:58:42.663 | 261830
2017-01-18 04:58:42.893 | 219835
2017-01-18 04:58:42.907 | 250165
2017-01-18 05:18:14.660 | 134253 <- batch no. 3
2017-01-18 05:18:14.663 | 134257
2017-01-18 05:18:14.667 | 134372
2017-01-18 05:18:15.040 | 181679
2017-01-18 05:18:15.043 | 226368
2017-01-18 05:18:15.043 | 227070

数据是批量生成的,批处理中的每一行都需要几毫秒才能生成。我试图将结果分组如下:

Date1                   | Date2                   | Count
------------------------+-------------------------+------
2017-01-18 01:12:34.107 | 2017-01-18 01:12:34.633 | 4
2017-01-18 04:58:42.660 | 2017-01-18 04:58:42.907 | 4
2017-01-18 05:18:14.660 | 2017-01-18 05:18:15.043 | 6

可以安全地假设,如果两个连续的行间隔超过1分钟,则它们属于不同的批次。

我尝试了涉及ROW_NUMBER函数的解决方案,但它们使用连续日期(两行之间的日期差异是固定的)。当差异模糊时,我怎样才能达到预期的效果?

请注意,批次可能会超过一分钟。例如,批次可能包含从2017-01-01 00:00:00开始到2017-01-01 00:05:00结束的行,包括~3000行,每行几十或几百毫秒。可以肯定的是,批次间隔至少1分钟。

3 个答案:

答案 0 :(得分:7)

试试这个:

select min(t.dateColumn) date1, max(t.dateColumn) date2, count(*)
from (
    select t.*, sum(val) over (
            order by t.dateColumn
            ) grp
    from (
        select t.*, case 
                when datediff(ms, lag(t.dateColumn, 1, t.dateColumn) over (
                            order by t.dateColumn
                            ), t.dateColumn) > 60000
                    then 1
                else 0
                end val
        from your_table t
        ) t
    ) t
group by grp;

产地:

enter image description here

使用分析函数lag()根据datecolumn与最后一批的差异标记下一批的开始,然后在其上使用分析sum()创建批次组,然后由它分组以找到所需的聚合。

由于DATETIME的舍入问题,群组中可能会出现一些错误分类。来自MSDN

  

日期时间值四舍五入为.000,.003或.007秒的增量,如下表所示。

enter image description here

以下是使用CTE重写的相同查询:

WITH cte1(DateColumn, ValueColumn) AS (
    -- Insert your query that returns a datetime column and any other column
    SELECT
        SomeDate,
        SomeValue
    FROM SomeTable
    WHERE SomeColumn IS NOT NULL
), cte2 AS (
    -- This query adds a column called "val" that contains
    -- 1 when current row date - previous row date > 1 minute
    -- 0 otherwise
    SELECT
        cte1.*,
        CASE WHEN DATEDIFF(MS, LAG(DateColumn, 1, DateColumn) OVER (ORDER BY DateColumn), DateColumn) > 60000 THEN 1 ELSE 0 END AS val
    FROM cte1
), cte3 AS (
    -- This query adds a column called "grp" that numbers 
    -- the groups using running sum over the "val" column
    SELECT
        cte2.*,
        SUM(val) OVER (ORDER BY DateColumn) AS grp
    FROM cte2
)
SELECT
    MIN(DateColumn) Date1,
    MAX(DateColumn) Date2,
    COUNT(ValueColumn) [Count]
FROM cte3
GROUP BY grp

答案 1 :(得分:0)

seconds移除millisecondsDateColumn并进行分组

  select min(DateColumn), 
         max(DateColumn), 
         count(*)
  from Yourtable
  group by DATEADD(MINUTE, DATEDIFF(MINUTE, 0, DateColumn), 0)

以下是截断日期时间秒的一些问题

Truncate seconds and milliseconds in SQL

A way to extract from a DateTime value data without seconds

答案 2 :(得分:-1)

如果您比较日期(60秒)之间的差距,这不起作用。但是你可以试试这个,如果你需要获得属于同一分钟X的记录。

SELECT
     [Date1]    =   MIN([DateColumn])
    ,[Date2]    =   MAX([DateColumn])
    ,[Count]    =   COUNT([DateColumn]) 
FROM
    [my_table]
GROUP BY
    DATEADD(mi, DATEDIFF(mi, 0, [DateColumn]), 0);