查找多行中连续日期的开始和停止日期

时间:2014-06-18 15:50:39

标签: sql sql-server gaps-and-islands row-number

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

我的表格包含以下数据:

> 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

提前致谢!

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

1 个答案:

答案 0 :(得分:5)

旧的答案有一个缺点:每行都只检查前一个,以验证周期是否重叠,如果前一行的周期超过逻辑不会考虑它。例如:

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,并且DateFrom

的值为Code的最大值
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 < 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分组获得结果