MySQL 或 MariaDB 将单行中的日期范围分散到多个行中
Posted
技术标签:
【中文标题】MySQL 或 MariaDB 将单行中的日期范围分散到多个行中【英文标题】:MySQL or MariaDB spread Date range in single row into multiple series of rows 【发布时间】:2022-01-20 05:09:47 【问题描述】:示例表如下,
CREATE TABLE TIMEOFF_INFO (
TIMEOFF_ID INT PRIMARY KEY,
TIMEOFF_BEGIN DATE NOT NULL,
TIMEOFF_END DATE NOT NULL
)
以及下面的示例行,
+------------+---------------+-------------+
| timeoff_id | timeoff_begin | timeoff_end |
+------------+---------------+-------------+
| 1 | 2021-10-01 | 2021-10-02 |
+------------+---------------+-------------+
| 2 | 2021-11-15 | 2021-11-15 |
+------------+---------------+-------------+
| 3 | 2021-12-18 | 2021-12-20 |
+------------+---------------+-------------+
我想要得到的是将上面的表格转换为下面的表格,以便我可以使用范围内的每个日期加入。
2021-10-01 (id: 1's begin date)
2021-10-02 (id: 1's end date)
2021-11-15 (id: 2's begin and end date)
2021-12-18 (id: 3's begin date)
2021-12-19
2021-12-20 (id: 3's end date)
有没有办法将单行中的日期范围扩展到一系列日期行?
【问题讨论】:
您是否有日历表或日期表可用于填充开始和结束时间超过 1 天的位置? 什么是精确的 DBMS 版本?显示SELECT VERSION();
的输出。
@P.Salmon 不存在其他特定的“日历”表
@Akina MariaDB 10.5.5
【参考方案1】:
WITH RECURSIVE
daterange AS ( SELECT MIN(TIMEOFF_BEGIN) dStart, MAX(TIMEOFF_END) dEnd
FROM TIMEOFF_INFO ),
calendar AS ( SELECT dStart `date`
FROM daterange
UNION ALL
SELECT `date` + INTERVAL 1 DAY
FROM calendar
WHERE `date` < ( SELECT dEnd FROM daterange ) )
SELECT calendar.`date`
FROM calendar
WHERE EXISTS ( SELECT NULL
FROM TIMEOFF_INFO
WHERE calendar.`date` BETWEEN TIMEOFF_INFO.TIMEOFF_BEGIN AND TIMEOFF_INFO.TIMEOFF_END )
需要 MariaDB 10.2+ 或 mysql 8+。
【讨论】:
这应该是我要找的。谢谢 :) 这似乎很适合与其他带有user_id
等列的外部表一起加入......【参考方案2】:
与@akina 大致相同,但不那么复杂。这里 cte 仅用于识别日期差异大于 1 的那些,然后加入日期表并联合。开始、中间和结束日期由用于排序的标志标识。 这可以在没有 cte 的情况下完成,但 cte 确实有助于澄清(我认为), 注意我已经稍微修改了示例数据
drop table if exists t;
create table t
(timeoff_id int, timeoff_begin date, timeoff_end date);
insert into t values
( 1 , '2020-10-01' , '2020-10-05'),
( 2 , '2020-11-15' , '2020-11-15'),
( 3 , '2020-12-18' , '2020-12-21');
with cte as
(select 2 as beg,timeoff_id , timeoff_begin, timeoff_end ,datediff(timeoff_end, timeoff_begin) diff
from t
where datediff(timeoff_end, timeoff_begin) > 1)
select cte.beg,cte.timeoff_id,dates.dte
from cte
join dates where dates.dte > cte.timeoff_begin and dates.dte <= date_add(cte.timeoff_begin, interval cte.diff -1 day)
union all
(select 1 as beg,timeoff_id , timeoff_begin dt from t
union all
select 3 ,timeoff_id , timeoff_end from t
)
order by timeoff_id, beg,dte;
+-----+------------+------------+
| beg | timeoff_id | dte |
+-----+------------+------------+
| 1 | 1 | 2020-10-01 |
| 2 | 1 | 2020-10-02 |
| 2 | 1 | 2020-10-03 |
| 2 | 1 | 2020-10-04 |
| 3 | 1 | 2020-10-05 |
| 1 | 2 | 2020-11-15 |
| 3 | 2 | 2020-11-15 |
| 1 | 3 | 2020-12-18 |
| 2 | 3 | 2020-12-19 |
| 2 | 3 | 2020-12-20 |
| 3 | 3 | 2020-12-21 |
+-----+------------+------------+
【讨论】:
表dates
是某种日历表,日历中的所有日期都作为行。我说的对吗?【参考方案3】:
你可以在同一张桌子上使用联合
select id, timeoff_begin timeoff, concat(id, ' begin date') msg
from TIMEOFF_INFO
union all
select id, timeoff_end, concat(id, ' end date')
from TIMEOFF_INFO
order by id, timeoff
【讨论】:
这将错过 2021 年 12 月 19 日(或任何其他开始和结束相隔超过 1 天的时间)以上是关于MySQL 或 MariaDB 将单行中的日期范围分散到多个行中的主要内容,如果未能解决你的问题,请参考以下文章
如果 mysql/mariadb 中的日期早于 1 小时,请更改列值?