MySQL构造具有重叠日期跨度的表行

时间:2015-12-04 06:46:29

标签: mysql sql

我有一个临时表,可用日期重叠。这些日期可以以任何方式组合重叠,因为它们适用于多个可用房间。

我想创建一个返回一组合并可用日期的查询。 (打开日期是可用日期;关闭不可用。)

例如,以下数据

+------------+------------+
|   opens    |   closes   |
+------------+------------+
| 2015-12-03 | 2015-12-05 |
| 2016-01-08 | 2016-01-15 |
| 2016-02-21 | 2016-02-27 |
| 2016-03-13 | 2016-03-24 |
| 2016-03-31 | 2016-04-02 |
| 2016-04-06 | 2016-04-15 |
| 2016-04-21 | 2016-12-03 |
| 2015-12-03 | 2015-12-09 |
| 2016-01-03 | 2016-01-06 |
| 2016-01-16 | 2016-02-08 |
| 2016-03-01 | 2016-03-06 |
| 2016-03-10 | 2016-12-03 |
+------------+------------+

应该返回:

+------------+------------+
|   opens    |   closes   |
+------------+------------+
| 2015-12-03 | 2015-12-09 |
| 2016-01-03 | 2016-01-06 |
| 2016-01-08 | 2016-01-15 |
| 2016-01-16 | 2016-02-08 |
| 2016-02-21 | 2016-02-27 |
| 2016-03-01 | 2016-03-06 |
| 2016-03-10 | 2016-12-03 |
+------------+------------+

感谢您的帮助!

1 个答案:

答案 0 :(得分:3)

这样做的一种方法是使用相关子查询:

SELECT DISTINCT
       (SELECT MIN(opens)
       FROM mytable AS t2
       WHERE t2.opens <= t1.closes AND t2.closes >= t1.opens) AS start,
       (SELECT MAX(closes)
       FROM mytable AS t2
       WHERE t2.opens <= t1.closes AND t2.closes >= t1.opens) AS end       
FROM mytable AS t1
ORDER BY opens

相关子查询的WHERE谓词:

t2.opens <= t1.closes AND t2.closes >= t1.opens

返回与当前记录相关的所有重叠记录。在这些记录中执行聚合,我们可以找到每个间隔的开始/结束日期:间隔的开始日期是所有重叠记录之间的最小opens日期,而结束日期是最大closes日期

Demo here

修改

上述解决方案无法使用如下所示的一系列间隔:

1. |-----------|
2. |----|
3.           |-----|

记录号码2,处理时,会产生有缺陷的开始/结束间隔。

这是使用变量的解决方案:

SELECT MIN(start) AS start, MAX(end) AS end
FROM (
  SELECT @grp := IF(@start = '1900-01-01' OR 
                   (opens <= @end AND closes >= @start), @grp, @grp+1) AS grp,        
         @start := IF(@start = '1900-01-01', opens, 
                      IF(opens <= @end AND closes >= @start, 
                         IF (@start < opens, @start, opens), opens)) AS start,
         @end := IF(@end = '1900-01-01', closes, 
                    IF (opens <= @end AND closes >= @start, 
                      IF (@end > closes, @end, closes), closes)) AS end                 
  FROM mytable
  CROSS JOIN (SELECT @grp := 1, @start := '1900-01-01', @end := '1900-01-01') AS vars
  ORDER BY opens, DATEDIFF(closes, opens) DESC) AS t
GROUP BY grp

这个想法是从最左边的opens/closes间隔开始。变量@start@end用于传播增量扩展(当正在处理新的重叠行时)在间隔链中的合并间隔。遇到非重叠间隔后,会初始化[@start - @end]以匹配此新间隔,grp会增加1。

Demo here