按SQL Server中的连续日期分组

时间:2015-09-02 07:20:39

标签: sql sql-server tsql gaps-and-islands

我的表格包含以下栏目:

[name_of_pos] varchar,
[date_from] datetime,
[date_to] datetime

以下是我的示例数据:

name_of_pos  date_from                 date_to
----------------------------------------------------------------
Asystent     2015-08-26 08:57:49.000   2015-09-04 08:57:49.000
Biuro        2015-09-01 08:53:32.000   2015-09-01 08:53:32.000
Biuro        2015-09-02 09:00:41.000   2015-09-02 09:00:41.000
Biuro        2015-09-03 11:46:03.000   2015-09-03 11:46:03.000
Biuro        2015-09-10 09:02:11.000   2015-09-15 09:02:11.000
Koordynator  2015-09-01 09:04:06.000   2015-09-01 09:04:06.000
Projektant   2015-08-31 08:59:46.000   2015-09-01 08:59:46.000
Projektant   2015-09-02 08:00:54.000   2015-09-02 08:00:54.000
Projektant   2015-09-14 12:34:50.000   2015-09-14 12:34:50.000

我想要返回的是每个date_from的日期范围(date_to到最大name_of_pos),但仅限于日期值是连续的(时间部分是不重要,结果可以忽略。

所需的输出是:

  name_of_pos   date_from   date_to
  ------------------------------------
  Asystent      2015-08-26  2015-09-04 
  Biuro         2015-09-01  2015-09-03 
  Biuro         2015-09-10  2015-09-15 
  Koordynator   2015-09-01  2015-09-01 
  Projektant    2015-08-31  2015-09-02 
  Projektant    2015-09-14  2015-09-14 

我尝试使用与此问题类似的解决方案:

How do I group on continuous ranges

但由于我有两个日期时间列,所以没有运气。

4 个答案:

答案 0 :(得分:1)

这是一个解决方案,使用cte迭代行(在订购后)并检查分组前的连续几天:

-- dummy table
CREATE TABLE #TableA
    (
      [name_of_pos] VARCHAR(11) ,
      [date_from] DATETIME ,
      [date_to] DATETIME
    );

-- insert dummy data
INSERT  INTO #TableA
        ( [name_of_pos], [date_from], [date_to] )
VALUES  ( 'Asystent', '2015-08-26 08:57:49', '2015-09-04 08:57:49' ),
        ( 'Biuro', '2015-09-01 08:53:32', '2015-09-01 08:53:32' ),
        ( 'Biuro', '2015-09-02 09:00:41', '2015-09-02 09:00:41' ),
        ( 'Biuro', '2015-09-03 11:46:03', '2015-09-03 11:46:03' ),
        ( 'Biuro', '2015-09-10 09:02:11', '2015-09-15 09:02:11' ),
        ( 'Koordynator', '2015-09-01 09:04:06', '2015-09-01 09:04:06' ),
        ( 'Projektant', '2015-08-31 08:59:46', '2015-09-01 08:59:46' ),
        ( 'Projektant', '2015-09-02 08:00:54', '2015-09-02 08:00:54' ),
        ( 'Projektant', '2015-09-14 12:34:50', '2015-09-14 12:34:50' );

-- new temp table used to add row numbers for data order
SELECT  name_of_pos, CAST(date_from AS DATE) date_from, CAST(date_to AS DATE) date_to,
        ROW_NUMBER() OVER ( ORDER BY name_of_pos, date_from ) rn
INTO    #temp
FROM    #TableA

-- GroupingColumn in cte used to identify and group consecutive dates
;WITH    cte
          AS ( SELECT   name_of_pos ,
                        date_from ,
                        date_to ,
                        1 AS GroupingColumn ,
                        rn
               FROM     #temp
               WHERE    rn = 1
               UNION ALL
               SELECT   t2.name_of_pos ,
                        t2.date_from ,
                        t2.date_to ,
                        CASE WHEN t2.date_from = DATEADD(day, 1, cte.date_to) 
                                  AND cte.name_of_pos = t2.name_of_pos
                             THEN cte.GroupingColumn
                             ELSE cte.GroupingColumn + 1
                        END AS GroupingColumn ,
                        t2.rn
               FROM     #temp t2
                        INNER JOIN cte ON t2.rn = cte.rn + 1
             )
    SELECT  name_of_pos, MIN(date_from) AS date_from, MAX(date_to) AS date_to
    FROM    cte
    GROUP BY name_of_pos, GroupingColumn

DROP TABLE #temp
DROP TABLE #TableA

产生您想要的输出:

name_of_pos date_from   date_to
Asystent    2015-08-26  2015-09-04
Biuro       2015-09-01  2015-09-03
Biuro       2015-09-10  2015-09-15
Koordynator 2015-09-01  2015-09-01
Projektant  2015-08-31  2015-09-02
Projektant  2015-09-14  2015-09-14

答案 1 :(得分:1)

这是一个缺口和岛屿问题。这是调整official 完成它的方法,这将作为解决方案进行检查:

;with 

cte as (
    SELECT *,
          dateadd( day,
           - (ROW_NUMBER() OVER (
               partition by name_of_pos
               ORDER BY t.date_from
              ) +                           -- here starts tuned part --
              isnull( 
               sum( datediff(day, date_from, date_to ) ) OVER (
                partition by name_of_pos
                ORDER BY t.date_from
                ROWS BETWEEN UNBOUNDED PRECEDING and 1 PRECEDING
              ) ,0)                         -- here ends tuned part --
           ),
           date_from
          ) as Grp
    FROM t
)  

SELECT name_of_pos
    ,min(date_from) AS date_from
    ,max(date_to) AS date_to
FROM cte
GROUP BY name_of_pos, Grp
ORDER BY name_of_pos, date_from

此处tested on sqlfiddle(包含一些不同的示例数据)。

答案 2 :(得分:0)

您可以使用<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="10dp" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="10dp" > <Button android:id="@+id/col1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:background="#F8BBD0" /> <Button android:id="@+id/col2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_weight="1" android:background="#FFCCBC" /> <Button android:id="@+id/col3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_weight="1" android:background="#E1BEE7" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="10dp" > <Button android:id="@+id/col4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:background="#BBDEFB" /> <Button android:id="@+id/col5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_weight="1" android:background="#C8E6C9" /> <Button android:id="@+id/col6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:layout_weight="1" android:background="#FFF9C4" /> </LinearLayout> ,但根据我的经验,最快的方法是在循环中使用更新:

cte

答案 3 :(得分:-2)

尝试一次:

SELECT name_of_pos, date_from,date_to
FROM table
ORDER BY
name_of_pos asc, date_from desc;