【问题标题】:SQL number of rows valid in a time range grouped by time按时间分组的时间范围内的 SQL 有效行数
【发布时间】:2020-03-10 03:42:25
【问题描述】:

SQL 服务器

我有一个带有 2 个时间戳的表,time_start 和 time_end。 例如

ID    time_start           time_end
----  -------------------  -------------------
1     2019-01-01 08:30:00  2019-01-01 09:40:00
2     2019-01-01 09:10:24  2019-01-01 15:14:19
3     2019-01-01 09:21:15  2019-01-01 09:21:19
4     2019-01-01 10:39:45  2019-01-01 10:58:12
5     2019-01-01 11:39:45  2019-01-01 11:40:10

我想对它们进行分组,这样我就可以将行数按可变时间间隔分组。 例如

time_interval         row_count
-------------------   ---------
2019-01-01 07:00:00   0
2019-01-01 08:00:00   1
2019-01-01 09:00:00   3
2019-01-01 10:00:00   2
2019-01-01 11:00:00   1
2019-01-01 12:00:00   0

我的时间间隔可能是 1 小时、1 分钟、30 分钟、1 天等......

将其视为登录/注销情况,我想看看用户在任何给定的分钟、小时、天等情况下是如何登录的......

【问题讨论】:

  • 外连接递归 cte(或表),返回所需的 time_interval 值。通过...分组。完成!
  • 听起来像是一个“计数重叠间隔”的问题。
  • 或者统计时间,然后分组。

标签: sql sql-server timestamp grouping intervals


【解决方案1】:

试试这个,

     DECLARE @start_date datetime='2019-01-01',
                @end_date datetime='2019-01-02',
                @i_minutes int=60

        DECLARE @t TABLE
        (
            id int identity(1,1),time_start datetime,time_end datetime
        )
        INSERT INTO @t(time_start,time_end)VALUES
        ('2019-01-01 08:30:00','2019-01-01 09:40:00'),
        ('2019-01-01 09:10:24','2019-01-01 15:14:19'),
        ('2019-01-01 09:21:15','2019-01-01 09:21:19'),
        ('2019-01-01 10:39:45','2019-01-01 10:58:12'),
        ('2019-01-01 11:39:45','2019-01-01 11:40:10')

        --SELECT @start_date=min(time_start),@end_date=max(time_end)
        --FROM @t

        ;WITH CTE_time_Interval AS
        (
            SELECT @start_date AS time_int,@i_minutes AS i_minutes
            UNION ALL
            SELECT dateadd(minute,@i_minutes,time_int),i_minutes+ @i_minutes
            FROM CTE_time_Interval
            WHERE time_int<=@end_date
        )
        ,CTE1 AS
        (
            SELECT ROW_NUMBER()OVER(ORDER BY time_int)AS r_no,time_int 
            FROM CTE_time_Interval
        )
        ,CTE2 AS
        (
            SELECT a.time_int AS Int_start_time,b.time_int AS Int_end_time 
            FROM CTE1 a
            INNER JOIN CTE1 b ON a.r_no+1=b.r_no
        )

SELECT a.Int_start_time,a.Int_end_time,sum(iif(b.time_start is not null,1,0)) AS cnt
FROM CTE2 a
LEFT JOIN @t b ON
(
    b.time_start BETWEEN a.Int_start_time AND a.Int_end_time 
    OR 
    b.time_end BETWEEN a.Int_start_time AND a.Int_end_time
    OR
    a.Int_start_time BETWEEN b.time_start AND b.time_end
    OR
    a.Int_end_time BETWEEN b.time_start AND b.time_end 
)
GROUP BY a.Int_start_time,a.Int_end_time

【讨论】:

  • 在您的解决方案中,您没有长时间记录的用户计数。例如,有一个用户在 09:10:24 登录并在 15:14:19 注销,因此您必须将该用户计数为 9:00、10:00、11:00、12:00、13 :00、14:00 和 15:00
  • @VíctorBeltrán,我做了一些改动,现在可以了
  • 我最终使用了这个版本。谢谢。
【解决方案2】:

您好,这是我的解决方法。
我用您的数据创建了一个表“测试”。

首先我得到最小和最大间隔,然后,我用 CTE 得到这些值之间的所有间隔。 最后,通过这个 CTE 和一个左连接以及 time_start 和 time_end 之间的间隔,我得到了答案。

这是1小时的间隔

DECLARE @minDate AS DATETIME;
DECLARE @maxDate AS DATETIME;

SET @minDate = (select case 
when (select min(time_start) from test) < (select min(time_end) from test) 
then (select min(time_start) from test)
else (select min(time_end) from test) end )
SET @minDate = FORMAT(@minDate, 'dd-MM.yyyy HH:00:00')

SET @maxDate = (select case 
when (select max(time_start) from test) > (select max(time_end) from test) 
then (select max(time_start) from test)
else (select max(time_end) from test) end )
SET @maxDate = FORMAT(@maxDate, 'dd-MM.yyyy HH:00:00')

;WITH Dates_CTE
     AS (SELECT @minDate AS Dates
         UNION ALL
         SELECT Dateadd(hh, 1, Dates)
         FROM   Dates_CTE
         WHERE  Dates < @maxDate)

SELECT d.Dates as time_interval, count(*) as row_count
FROM   Dates_CTE d
LEFT JOIN test t on d.Dates 
between (FORMAT(t.time_start, 'dd-MM.yyyy HH:00:00')) 
and (FORMAT(t.time_end, 'dd-MM.yyyy HH:00:00'))  
GROUP BY d.Dates

对于 10 分钟的间隔,您需要进行一些更改。
首先我格式化日期得到分钟(dd-MM.yyyy HH:mm:00 instead of dd-MM.yyyy HH:00:00) 在左侧连接中,我接近 time_start 和 time_end 到他们的 10 分钟时间(9:30:00 中的 9:32:00)(dateadd(minute, 10 * (datediff(minute, 0, time_start) / 10), 0)):

DECLARE @minDate AS DATETIME;
DECLARE @maxDate AS DATETIME;

SET @minDate = (select case 
when (select min(time_start) from test) < (select min(time_end) from test) 
then (select min(time_start) from test)
else (select min(time_end) from test) end )
SET @minDate = FORMAT(@minDate, 'dd-MM.yyyy HH:mm:00')

SET @maxDate = (select case 
when (select max(time_start) from test) > (select max(time_end) from test) 
then (select max(time_start) from test)
else (select max(time_end) from test) end )
SET @maxDate = FORMAT(@maxDate, 'dd-MM.yyyy HH:mm:00')

;WITH Dates_CTE
     AS (SELECT @minDate AS Dates
         UNION ALL
         SELECT Dateadd(minute, 10, Dates)
         FROM   Dates_CTE
         WHERE  Dates < @maxDate)

SELECT d.Dates as time_interval, count(*) as row_count
FROM   Dates_CTE d
LEFT JOIN test t on d.Dates 
between dateadd(minute, 10 * (datediff(minute, 0, time_start) / 10), 0) 
and dateadd(minute, 10 * (datediff(minute, 0, time_end) / 10), 0)
GROUP BY d.Dates

最后我每隔 1 小时得到这个结果:

+---------------------+-----------+
|    time_interval    | row_count |
+---------------------+-----------+
| 01/01/2019 08:00:00 |         1 |
| 01/01/2019 09:00:00 |         3 |
| 01/01/2019 10:00:00 |         2 |
| 01/01/2019 11:00:00 |         2 |
| 01/01/2019 12:00:00 |         1 |
| 01/01/2019 13:00:00 |         1 |
| 01/01/2019 14:00:00 |         1 |
| 01/01/2019 15:00:00 |         1 |
+---------------------+-----------+

我希望它对你有用。

【讨论】:

    【解决方案3】:

    您需要指定时间间隔。其余的是LEFT JOIN/GROUP BY 或相关子查询:

    with dates as (
          select convert(datetime, '2019-01-01 07:00:00') as dt
          union all
          select dateadd(hour, 1, dt)
          from dates
          where dt < '2019-01-01 12:00:00'
         )
    select dates.dt, count(t.id)
    from dates left join
         t
         on dates.dt < t.time_end and
            dates.dt >= dateadd(hour, 1, t.time_start)
    group by dates.dt
    order by dates.dt;
    

    如果您有大量数据和大量时间段,您可能会发现它的性能很差。如果是这种情况,请提出一个问题,并提供有关大小和性能的更多信息。

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 1970-01-01
      • 1970-01-01
      • 2018-04-13
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-23
      相关资源
      最近更新 更多