查找多行中连续日期的开始和结束日期

Posted

技术标签:

【中文标题】查找多行中连续日期的开始和结束日期【英文标题】:find start and stop date for contiguous dates in multiple rows 【发布时间】:2014-06-18 15:50:38 【问题描述】:

我在显示表格中的正确数据时遇到了一些问题。 我也不确定要搜索什么。 我不确定 min(column) 或 max(column) 是否会帮助我。让我们看看我是否可以解释我的问题。

我的表包含以下数据:

> Code (nvarchar) | DateFrom (datetime) | DateTo (datetime)
> ========================================================= 
> 3006            | 2014-06-18 07:00:00 | 2014-06-18 08:00:00 
> 3006            | 2014-06-18 09:00:00 | 2014-06-18 22:00:00 
> 1006            | 2014-06-18 07:00:00 | 2014-06-18 09:00:00
> 1006            | 2014-06-18 08:00:00 | 2014-06-18 08:30:00 
> 1006            | 2014-06-18 08:10:00 | 2014-06-18 18:00:00

我将通过一个视图来介绍这一点。 它将按代码分组。

我想要的是这个输出:

> Code | DateFrom            | DateTo
> =========================================================
> 3006 | 2014-06-08 07:00:00 | 2014-06-18 08:00:00
> 3006 | 2014-06-18 09:00:00 | 2014-06-18 22:00:00
> 1006 | 2014-06-18 07:00:00 | 2014-06-18 18:00:00

如您所见,DateTo 和 DateFrom 之间是否存在间隙,我希望它显示为两行。但是,如果下一个具有相同代码的“DateFrom”在 DateTo 结束之前(或同时)开始,我希望改为显示“DateTo”。

在这种情况下,我不知道如何使用函数 max() 或 min()。因为时间段内可能存在间隙。

你们有什么线索吗?

我使用的是 MS SQL 2012

提前致谢!

编辑:如评论。岛屿可能是我的解决方案?

【问题讨论】:

您尝试过使用 LAG() 或 LEAD() Fcts 吗? 不,我没有。我不熟悉那个功能。我会谷歌它。 :) 我不认为这是我需要的。即使 dateto 和 datefrom 之间存在间隔,它也会捕获下一个 dateto。 我认为您想要的称为gaps-and-islands 技术来识别数据中的运行/差距。 那个人可能会以某种方式解决问题。我去看看! 【参考方案1】:

旧答案有一个弱点:每行仅与前一行检查以验证周期是否重叠,如果较早的行有一个持续时间更长的周期,则逻辑不会考虑它。例如:

Code | DateStart           | DateFrom            | Overlap
-----+---------------------+---------------------+---------
1006 | 2014-06-18 07:00:00 | 2014-06-18 19:00:00 | 0 
1006 | 2014-06-18 08:10:00 | 2014-06-18 10:00:00 | 1 
1006 | 2014-06-18 16:00:00 | 2014-06-18 20:30:00 | 0 <- don't overlap with 
                                                        previous but overlap
                                                        with the first

要改进 PrevStop 需要变为 LastStop 并具有 Code 的前一个 DateFrom 的最大值

With N AS (
  SELECT Code, DateFrom, DateTo
       , LastStop = MAX(DateTo) 
                    OVER (PARTITION BY Code ORDER BY DateFrom, DateTo 
                          ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
  FROM   Table1
), B AS (
  SELECT Code, DateFrom, DateTo
       , Block = SUM(CASE WHEN LastStop Is Null Then 1
                          WHEN LastStop < DateFrom Then 1
                          ELSE 0
                    END)
                 OVER (PARTITION BY Code ORDER BY DateFrom, LastStop)
  FROM   N
)
SELECT Code
     , MIN(DateFrom) DateFrom
     , MAX(DateTo) DateTo
FROM   B
GROUP BY Code, Block
ORDER BY Code, Block

SQLFiddle Demo

需要ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING 才能从MAX 中删除当前行。


旧答案

此查询仅适用于不完全在先例中的每个句点。

这个想法是检查每一行是否与下一个/上一个链接。 如果行被链接,它们形成一个块,它们将被组合在一起以获得第一个 DateFrom 和最后一个 DateTo

With N AS (
  SELECT Code, DateFrom, DateTo
       , PrevStop = LAG(DateTo, 1, NULL) 
                    OVER (PARTITION BY Code ORDER BY DateFrom)
  FROM   Table1
), B AS (
  SELECT Code, DateFrom, DateTo
       , Block = SUM(CASE WHEN PrevStop Is Null Then 1
                          WHEN PrevStop < DateFrom Then 1
                          ELSE 0
                     END)
                 OVER (PARTITION BY Code ORDER BY PrevStop)
  FROM   N
)
SELECT Code
     , MIN(DateFrom) DateFrom
     , MAX(DateTo) DateTo
FROM   B
GROUP BY Code, Block
ORDER BY Code, Block

SQLFiddle demo 添加了一些数据以检查同一代码/天的更多块

块启动器的查询搜索检查每一行是否是代码的第一行 (PrevStop IS NULL) 或是否在前一行之外 (PrevStop &lt; DateFrom)。

窗口化的SUM 仅检索ORDER 的前一行,以创建链接数据块的常数值,例如我们将获得的测试数据

Code | DateStart           | DateFrom            | Starter | Block
-----+---------------------+---------------------+---------+------
1006 | 2014-06-18 07:00:00 | 2014-06-18 09:00:00 |       1 |     1
1006 | 2014-06-18 08:10:00 | 2014-06-18 06:00:00 |       0 |     1
1006 | 2014-06-18 08:00:00 | 2014-06-18 08:30:00 |       0 |     1
1006 | 2014-06-18 07:00:00 | 2014-06-18 07:30:00 |       1 |     2
1006 | 2014-06-18 08:00:00 | 2014-06-18 08:30:00 |       1 |     3
1006 | 2014-06-18 08:10:00 | 2014-06-18 09:00:00 |       0 |     3
3006 | 2014-06-18 07:00:00 | 2014-06-18 08:00:00 |       1 |     1
3006 | 2014-06-18 09:00:00 | 2014-06-18 10:00:00 |       1 |     2

CodeBlock分组得到结果

【讨论】:

似乎这并不成功。我得到多行相同的代码,即使它们在同一个时间段内。 似乎问题出现了,因为有超过 1 个具有相同的开始时间? sqlfiddle.com/#!6/a5c59/1

以上是关于查找多行中连续日期的开始和结束日期的主要内容,如果未能解决你的问题,请参考以下文章

从连续日期中查找最近的开始日期和结束日期

查找每个员工的项目开始日期和结束日期(即开始日期和结束日期应该是连续的,在天/月/年中没有任何中断)

如何在 SQL Server 的多行中查找连续的日期

如何在 Presto 中获取连续日期,其中一列中的开始日期和另一列中的结束日期

根据日期范围查找至少 2 个连续项目

用于删除重复(连续)记录的 SQL,但将最小日期存储在开始日期和最大日期作为结束日期