【问题标题】:Group Rows By Date Range按日期范围分组行
【发布时间】:2019-02-19 15:20:41
【问题描述】:

我想知道某个用户在特定秒数(例如 10 秒)内存在多少行。所以,鉴于这些数据:

UserId  CreatedDate
4.........2017-01-11 01:40:19:077
4.........2017-01-11 01:40:19:437
4.........2017-01-11 01:40:20:077
4.........2017-01-11 01:50:19:077
4.........2017-01-11 02:40:19:077
4.........2017-01-11 02:40:19:437
4.........2017-01-11 02:40:20:077
4.........2017-01-11 02:40:20:437
4.........2017-01-11 02:40:21:077
4.........2017-01-11 02:40:22:077
4.........2017-01-11 02:40:23:077
4.........2017-01-11 03:15:19:077
4.........2017-01-11 03:40:19:077
4.........2017-01-11 04:40:19:077

前三行将组合在一起,第 5-11 行将组合在一起(因为它们都在 10 秒内)。

我尝试了类似的方法,但这只给了我两个一组的行。我想知道 10 秒范围内的所有行。

;WITH CTE AS
(
    SELECT UserId
        ,CreatedDate
        ,ISNULL(LAG(CreatedDate) OVER (Partition BY UserId ORDER BY CreatedDate), '1/1/2000') AS PriorCreatedDate
    FROM Foo
)
SELECT *
FROM CTE
WHERE DATEDIFF(SECOND,PriorCreatedDate,CreatedDate) <= 1
ORDER BY UserId, CreatedDate

这可能吗?

【问题讨论】:

  • “都在 10 秒之内”到底是什么意思?如果一行有他的值2017-01-11 01:50:00,它会在第一组吗?
  • 第一组大约是 1:40,所以不,那是 10 分钟后。它也不适合 1:50:19,因为那是 19 秒后。
  • 是将每个用户的个人活动分解为它自己的前:10 秒间隔,或全部基于 SAME Master 10 秒基础。完全不同的结果
  • 10 秒间隔是全局的。
  • @BobHorn 。 . .正如您所指定的问题,这需要递归 CTE - 这相当昂贵。

标签: sql sql-server date sql-server-2014


【解决方案1】:

这种方式性能更高:

SELECT UserId, Date = Min( --Min or any value in the group give the same value
    case when DATEDIFF(SECOND, PreviousDate, creationDate) <= 10 
        then creationDate
        else PreviousDate
        end
)

FROM (
SELECT *,
PreviousDate = LAG(CreatedDate, 1, CreatedDate) OVER (Partition BY UserId ORDER BY CreatedDate desc), 
FROM CTE
)

GROUP BY UserId, case when DATEDIFF(SECOND, PreviousDate, creationDate) <= 10 
        then creationDate
        else PreviousDate
        end
ORDER BY UserId, Date

【讨论】:

    【解决方案2】:

    也许是这样的……

    示例

    Declare @YourTable Table ([UserId] int,[CreatedDate] datetime)
    Insert Into @YourTable Values 
     (4,'2017-01-11 01:40:19:077')
    ,(4,'2017-01-11 01:40:19:437')
    ,(4,'2017-01-11 01:40:20:077')
    ,(4,'2017-01-11 01:50:19:077')
    ,(4,'2017-01-11 02:40:19:077')
    ,(4,'2017-01-11 02:40:19:437')
    ,(4,'2017-01-11 02:40:20:077')
    ,(4,'2017-01-11 02:40:20:437')
    ,(4,'2017-01-11 02:40:21:077')
    ,(4,'2017-01-11 02:40:22:077')
    ,(4,'2017-01-11 02:40:23:077')
    ,(4,'2017-01-11 03:15:19:077')
    ,(4,'2017-01-11 03:40:19:077')
    ,(4,'2017-01-11 04:40:19:077')
    
    ;with cte as (
        Select *
              ,Flg = case when datediff(SECOND,lag(CreatedDate,1,CreatedDate) over (Partition By UserID Order by CreatedDate),CreatedDate ) >=10 then 1 else 0 end
         From @YourTable
    ) 
    Select UserID
          ,DateR1 = min(CreatedDate)
          ,DateR2 = max(CreatedDate)
          ,RecCnt = sum(1)
     From (Select *,Grp=sum(Flg) over (partition by UserID order by CreatedDate) From  cte ) A
     Group by UserID,Grp
    

    退货

    UserID  DateR1                      DateR2                      RecCnt
    4       2017-01-11 01:40:19.077     2017-01-11 01:40:20.077     3
    4       2017-01-11 01:50:19.077     2017-01-11 01:50:19.077     1
    4       2017-01-11 02:40:19.077     2017-01-11 02:40:23.077     7
    4       2017-01-11 03:15:19.077     2017-01-11 03:15:19.077     1
    4       2017-01-11 03:40:19.077     2017-01-11 03:40:19.077     1
    4       2017-01-11 04:40:19.077     2017-01-11 04:40:19.077     1
    

    编辑 - 要求的注释

    如果你要执行

    ;with cte as (
        Select *
              ,Flg = case when datediff(SECOND,lag(CreatedDate,1,CreatedDate) over (Partition By UserID Order by CreatedDate),CreatedDate ) >=10 then 1 else 0 end
         From @YourTable
    ) 
    Select *
          ,Grp=sum(Flg) over (partition by UserID order by CreatedDate) 
    From  cte   
    

    结果是:

    注意 Flg 和 Grp 列。 Grp 列本质上是 Flg 列的累计。

    【讨论】:

    • 您的意思是对 DateR2 使用 MAX 吗?什么是FLG?什么是 DateR1?日期范围 1?
    • @BobHorn 是的,我做到了...:0
    • 这看起来有效。我还不确定如何,大声笑,但我正在研究它。没关系,但我在最后添加了这个,因为它确实是我关心的:` HAVING SUM(1) > 10 ORDER BY SUM(1) DESC`
    • 我认为这也可以。这是一组通常称为 SQL 排名问题的解决方案。基本思想是您需要根据一些 SQL 未内置的标准对行进行排名。如果您按天/月/年进行分组,则可以按这些增量进行分组,但由于您按“10 秒增量”分组,因此使用起来并不容易。因此,典型的解决方案是执行类似嵌套查询的操作,该查询分配各个记录的排名/排序,然后外部查询根据该排名进行分组。所以 John 的解决方案是通过 CTE 实现的。
    • @BobHorn BINGO。我们都站在巨人的肩膀上。 :)
    猜你喜欢
    • 2013-11-04
    • 2021-11-16
    • 2013-05-19
    • 2010-10-31
    • 2016-09-22
    • 2019-02-14
    • 2015-01-03
    相关资源
    最近更新 更多