在 SQL Server 2012+ 中选择连续期间的最小开始和最大结束

Posted

技术标签:

【中文标题】在 SQL Server 2012+ 中选择连续期间的最小开始和最大结束【英文标题】:Select min start and max end for consecutive periods in SQL Server 2012+ 【发布时间】:2018-11-13 01:04:44 【问题描述】:

我在 SQL Server 2012 中有一个表,其中的事件日志格式如下:

+=====+=============================+=============================+======+
| ID1 |       start_time_utc        |        end_time_utc         | ID2  |
+=====+=============================+=============================+======+
|  57 | 2018-11-11 11:00:00.0000000 | 2018-11-11 11:00:28.0012900 |   15 |
|  57 | 2018-11-11 11:00:28.0012900 | 2018-11-11 11:01:29.0543947 | 1020 |
|  57 | 2018-11-11 11:01:29.0543947 | 2018-11-11 11:02:28.1923079 |   16 |
|  57 | 2018-11-11 11:02:28.1923079 | 2018-11-11 11:04:28.3367626 |   16 |
|  57 | 2018-11-11 11:04:28.3367626 | 2018-11-11 11:05:28.5307626 | 1020 |
| 103 | 2018-11-10 20:00:00.0000000 | 2018-11-11 03:00:00.0000000 |   15 |
| 103 | 2018-11-11 03:00:00.0000000 | 2018-11-11 10:57:00.8175737 |   15 |
| 103 | 2018-11-11 10:57:00.8175737 | 2018-11-11 10:57:27.8322749 | 1017 |
| 103 | 2018-11-11 10:57:27.8322749 | 2018-11-11 11:00:00.0000000 |   15 |
| 103 | 2018-11-11 11:00:00.0000000 | 2018-11-11 11:00:31.9916890 |   15 |
+-----+-----------------------------+-----------------------------+------+

对于给定的 ID1,结束日期与下一个事件的开始日期相匹配。我想通过匹配 ID1 和 ID2 列对数据进行分区,并为连续事件的每个分区选择开始日期和结束日期。所以结果应该是:

+=====+=============================+=============================+======+
| ID1 |       start_time_utc        |        end_time_utc         | ID2  |
+=====+=============================+=============================+======+
|  57 | 2018-11-11 11:00:00.0000000 | 2018-11-11 11:00:28.0012900 |   15 |
|  57 | 2018-11-11 11:00:28.0012900 | 2018-11-11 11:01:29.0543947 | 1020 |
|  57 | 2018-11-11 11:01:29.0543947 | 2018-11-11 11:04:28.3367626 |   16 |
|  57 | 2018-11-11 11:04:28.3367626 | 2018-11-11 11:05:28.5307626 | 1020 |
| 103 | 2018-11-10 20:00:00.0000000 | 2018-11-11 10:57:00.8175737 |   15 |
| 103 | 2018-11-11 10:57:00.8175737 | 2018-11-11 10:57:27.8322749 | 1017 |
| 103 | 2018-11-11 10:57:27.8322749 | 2018-11-11 11:00:31.9916890 |   15 |
+-----+-----------------------------+-----------------------------+------+

我显然不能使用简单的group by,也不知道如何写partition by query。感谢您的帮助。

【问题讨论】:

您是否有某种列来检查这是下一个事件还是上一个事件?我只是想了解是否有这样的领域会更容易。我试图用相同的 ID1 和 ID2 按开始日期订购,但看看你的输出,我认为它不能用日期来完成。 @Avi:不,唯一的下一个/上一个是每个开始和结束时间 【参考方案1】:

这是一个更新版本,适用于给定的数据集,还可以处理在我测试过的所有情况下,id1、id2 的序列大于 2 的情况。它比我原来的答案简单得多。将 tstGrouping 替换为您的表名。

;with p as
(
  select
   ROW_NUMBER () over (order by id1, start_time_utc) as row_num,
   ROW_NUMBER () over (order by id1,id2, start_time_utc) as row_num2,
    *
  from
    tstgrouping x1
)
select 
  id1,
  min(start_time_utc) as start_time_utc,
  max(end_time_utc) as end_time_utc,
  id2
from p
  group by
row_num - row_num2,id1,id2
order by 
id1, start_time_utc

【讨论】:

效果很好,谢谢。很好很简单。我早些时候试图通过尝试将结束时间加入到下一个开始时间来过度复杂化。这种方法不关心结束时间是否有匹配的开始时间,对吗? 我在第一次尝试时也过于复杂了,这并不真正关心时间它只是利用连续 id1,id2 对在按 id1、start_time 和 id1,id2 排序时将具有相同的行偏移量这一事实,开始时间【参考方案2】:
;with base as
(
Select *
,row_number()over(partition by id1, 1d2 order by start_time_utc asc) ordstart
,row_number()over(partition by id1, id2, order by end_date_utc desc) ordend
)

Select * from 
base b1
inner join base b2 on b1.id1 = b2.id1 and b1.id2 = b2.id2 ans b1.ordstart = b2.ordend
Where b1.ordstart = 1

【讨论】:

通过我的手机完成并且未经测试,但我很确定这应该可以工作。 快速测试说这个答案过滤掉了太多行,每个id1,id2组合只返回一个。 好电话。因此,您必须在 b1.start_time = b2.end_time 上将 cte 与自身连接起来,并将 id 与 case 语句进行比较。可能已经运行了一段时间循环,因为我不相信可以存在多少个连续的 id 对。我肯定会有,但如果明天早上之前没有接受的答案,我会在工作休息时整理一些东西。

以上是关于在 SQL Server 2012+ 中选择连续期间的最小开始和最大结束的主要内容,如果未能解决你的问题,请参考以下文章

在 Access 中使用 ODBC 连接到 MS SQL Server 2012:手动调用查询和在 VBA 中调用查询之间的巨大时间差异

通过 Internet 连接到 SQL Server Express 2012

MS SQL Server 2012:网络访问配置不起作用

使用 sqlalchemy 和 pyodbc 连接到 SQL Server 2012

如何在火花中使用 sql server 2012 jdbc jar

SQL Server Express 2012 错误:无法连接到本地数据库