有没有一种简单的方法可以在SQL中对插值的开始/结束日期进行分组?

时间:2017-01-18 16:04:50

标签: sql

基本上,假设我们有一个包含开始和结束日期的项目列表。像这样:

| Name   | Start     | Finish   |
---------------------------------
| Item 1 | Jan 1     | Jan 4    |
| Item 2 | Jan 3     | Jan 5    |
| Item 3 | Jan 4     | Jan 7    |

| Item 4 | Jan 10    | Jan 14   |

| Item 5 | Jan 15    | Jan 17   |
| Item 6 | Jan 17    | Jan 20   |

| Item 7 | Jan 25    | Jan 27   |
| Item 8 | Jan 26    | Jan 26   |
| Item 9 | Jan 27    | Jan 30   |

如上所示,如果项目的开始日期在组的其他成员的最低开始日期和最高结束日期之间,我基本上需要获取项目在组中的分组的日期。 1月1日至7日,1月10日至14日,1月15日至20日,以及1月25日至30日。有没有更简单的方法在SQL中执行此操作而不仅仅是强制执行它?

谢谢!

2 个答案:

答案 0 :(得分:2)

您所追求的通常被称为会话化,例如点击流分析学科:我们将同一用户在同一网站上的点击分组在一起,点击之间的不活动时间不到30分钟,分析此类会话中的行为。

让我给你快速回答 - 但它只适用于Vertica,使用CONDITIONAL_TRUE_EVENT分析函数:

https://my.vertica.com/docs/8.0.x/HTML/index.htm#Authoring/SQLReferenceManual/Functions/TimeSeries/CONDITIONAL_TRUE_EVENTAnalytic.htm

WITH
-- input data
foo(name,start,finish) AS (
          SELECT 'Item 1',DATE '2017-01-01',DATE '2017-01-04'
UNION ALL SELECT 'Item 2',DATE '2017-01-03',DATE '2017-01-05'
UNION ALL SELECT 'Item 3',DATE '2017-01-04',DATE '2017-01-07'

UNION ALL SELECT 'Item 4',DATE '2017-01-10',DATE '2017-01-14'

UNION ALL SELECT 'Item 5',DATE '2017-01-15',DATE '2017-01-17'
UNION ALL SELECT 'Item 6',DATE '2017-01-17',DATE '2017-01-20'

UNION ALL SELECT 'Item 7',DATE '2017-01-25',DATE '2017-01-27'
UNION ALL SELECT 'Item 8',DATE '2017-01-26',DATE '2017-01-26'
UNION ALL SELECT 'Item 9',DATE '2017-01-27',DATE '2017-01-30'
)

SELECT
  CONDITIONAL_TRUE_EVENT(start::TIMESTAMP > LAG(finish::TIMESTAMP))
OVER(PARTITION BY 1 ORDER BY start) AS grp_id
, *
FROM foo;

每次括号之间的表达式求值为TRUE时,结果是从0开始递增1并在每次PARTITION BY值改变时复位为0(这里是常量):

grp_id|name  |start     |finish
     0|Item 1|2017-01-01|2017-01-04
     0|Item 2|2017-01-03|2017-01-05
     0|Item 3|2017-01-04|2017-01-07
     1|Item 4|2017-01-10|2017-01-14
     2|Item 5|2017-01-15|2017-01-17
     2|Item 6|2017-01-17|2017-01-20
     3|Item 7|2017-01-25|2017-01-27
     3|Item 8|2017-01-26|2017-01-26
     4|Item 9|2017-01-27|2017-01-30

现在。您的数据库平台是否支持通用分析功能,或者不支持,例如,MySQL?根据您的答案,我将根据OLAP函数或基于相关的子选择重写此内容。

但无论如何,这将花费更多时间......

快乐的比赛 -

Marco the Sane

答案 1 :(得分:2)

显然,CONDITIONAL_TRUE_EVENT是最优雅的解决方案,但由于SQL本身不支持这种解决方案,因此可以这样做(首选的CTE超过子查询)。

;WITH DatesWithLag AS (
    SELECT 
      StartDate 
    , StopDate 
    , LAG(StopDate) OVER (ORDER BY StartDate) AS PrevStop 
    , DATEDIFF( 
         day 
      , LAG(StopDate) OVER (ORDER BY StartDate) 
      , StartDate 
      ) AS DayDiff
    FROM Dates
), 
Cond1 AS (
    SELECT 
      StartDate 
    , StopDate 
    , DayDiff 
    , (CASE WHEN DayDiff > 0 THEN 1 END) Change
    FROM DatesWithLag
),
Cond2 AS (
    SELECT 
      StartDate 
    , StopDate 
    , COUNT(Change) OVER ( 
        ORDER BY StartDate  
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW 
      ) Grp     --  
    FROM Cond1
)
SELECT Cond1.* 
     , Cond2.Grp 
FROM Cond1
    JOIN Cond2 ON Cond2.StartDate = Cond1.StartDate
ORDER BY Cond1.StartDate
GO

另一种选择是将CONDITIONAL_TRUE_EVENT实施为CLR function.