我试图弄清楚如何在MySQL中进行递归查询。
我有一张桌子,我在其中跟踪某人获得认证的日期。相同的证书可能会重叠-说您在结束之前需要重新认证才能保持认证!
这里是跟踪用户证书的表的示例。 user
和cert
表是非常基本的,对于这个问题来说是不必要的。
CREATE TABLE user_cert (
id INT AUTO_INCREMENT,
user INT NOT NULL,
cert INT NOT NULL,
date_begin DATE NOT NULL,
date_end DATE NOT NULL,
PRIMARY KEY (id)
)
例如,对于一个用户,这可能是值。
| id | user | cert | date_begin | date_end |
|----|------|------|------------|------------|
| 01 | 100 | 1000 | 2018-12-01 | 2019-02-01 |
| 02 | 100 | 1000 | 2019-04-01 | 2019-07-01 |
| 03 | 100 | 1000 | 2019-06-01 | 2019-09-01 |
| 04 | 100 | 1000 | 2019-08-01 | 2019-12-01 |
| 05 | 100 | 1000 | 2019-07-01 | 2019-11-01 |
这里是视觉表示。 ○
代表每个认证周期的开始和结束日期。 ●
代表我要查询的开始和结束日期。可能有许多重叠。 |
代表我要检查的日期。
id--12--01--02--03--04--05--06--07--08--09--10--11--12--
01 ○-------○ |
02 ●-----------○
03 ○-----------○
04 | ○---------------●
05 | ○---------------○
编写查询以获取与我正在检查的日期重叠的行的最小日期和最大日期很简单(2019-06-15
。
SELECT user, MIN(date_begin), MAX(date_end)
FROM user_cert
WHERE user = 100 AND '2019-06-15' BETWEEN date_begin AND date_end
GROUP BY user;
由于两行重叠,因此将产生1, '2019-04-01', '2019-09-01'
。
我的问题是:是否可以从单个查询中获取1, '2019-04-01', '2019-12-01'
以获取连续认证的最小和最大日期?如果我多次加入user_cert
表,我知道该怎么做,但这只会允许这么多的扩展层。如果我不知道存在多少可能的扩展重叠,该怎么办?
对于mysql 8.0,请尝试以下操作:
CREATE TABLE IF NOT EXISTS user_cert (
`user` int not null,
date_begin date not null,
date_end date not null
);
INSERT user_cert(`user`,date_begin,date_end)
VALUES (100,'2018-12-01','2019-02-01')
,(100,'2019-04-01','2019-07-01')
,(100,'2019-06-01','2019-09-01')
,(100,'2019-08-01','2019-12-01')
,(100,'2019-07-01','2019-11-01')
,(101,'2019-04-01','2019-07-01')
,(101,'2019-06-01','2019-09-01')
,(101,'2019-08-01','2019-12-01')
,(101,'2019-01-01','2019-03-01');
;WITH RECURSIVE cte_r(`user`,date_begin,date_end,rid)
AS
(
SELECT `user`, date_begin, date_end, ROW_NUMBER() OVER(PARTITION BY `user` ORDER BY date_begin) AS rid
FROM user_cert
),
cte_range(`user`,date_begin, date_end, rid)
AS
(
SELECT `user`,date_begin, date_end, rid
FROM cte_r
WHERE rid=1
UNION ALL
SELECT p.`user`, CASE WHEN c.date_begin<p.date_end THEN p.date_begin ELSE c.date_begin END AS date_begin,
c.date_end,c.rid
FROM cte_range p
INNER JOIN cte_r c
ON p.`user`=c.`user`
AND p.rid+1=c.rid
)
SELECT `user`,date_begin,MAX(date_end) AS date_end FROM Cte_range GROUP BY `user`,date_begin;