SQL 在按时间分组的时间范围内有效的行数
SQL number of rows valid in a time range grouped by time
SQL 服务器
我有一个带有 2 个时间戳的 table,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 天等...
将其视为 log-in/log-out 情况,我想了解在任何给定的分钟、小时、天等情况下,用户的登录情况...
试试这个,
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
嗨,这是我的解决方法。
我用你的数据创建了一个 table "test"。
首先我得到最小和最大间隔,然后我用 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 |
+---------------------+-----------+
希望对你有用。
您需要指定时间间隔。其余的是 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;
如果您有很多数据和很多时间段,您可能会发现它的性能很差。如果是这种情况,请提出 new 问题,并提供有关大小和性能的更多信息。
SQL 服务器
我有一个带有 2 个时间戳的 table,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 天等...
将其视为 log-in/log-out 情况,我想了解在任何给定的分钟、小时、天等情况下,用户的登录情况...
试试这个,
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
嗨,这是我的解决方法。
我用你的数据创建了一个 table "test"。
首先我得到最小和最大间隔,然后我用 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 |
+---------------------+-----------+
希望对你有用。
您需要指定时间间隔。其余的是 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;
如果您有很多数据和很多时间段,您可能会发现它的性能很差。如果是这种情况,请提出 new 问题,并提供有关大小和性能的更多信息。