【问题标题】:Recursive MySQL Query for Variable Overlaps用于变量重叠的递归 MySQL 查询
【发布时间】:2020-03-27 07:20:11
【问题描述】:

我正在尝试弄清楚如何在 MySQL 中进行递归查询。

我有一张表格,用于跟踪某人获得认证的日期。相同的认证可能会重叠 - 假设您在结束之前获得了重新认证以保持您的认证!

以下是跟踪用户认证的表格示例。 usercert 表非常基本,对于这个问题来说是不必要的。

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 表,我知道该怎么做,但这只会允许这么多层次的扩展。如果我不知道存在多少可能的扩展重叠怎么办?

【问题讨论】:

  • 所以,一个间隙和孤岛问题
  • 这类似于预订可用性系统的工作方式。如果您的开始日期和结束日期作为开始事件和结束事件作为单独的记录存储在数据库中,则跟踪可用性会容易得多,每个记录都有一个修饰符值 - 即 +1 用于开始,-1 用于结束。然后,您可以简单地按日期顺序查询它们,并通过它们添加/减去值并关注总数。每当它变为零时,您就知道您已经达到了一个未覆盖的日期范围,该日期范围会持续到下一条记录,这应该会再次使其恢复到 +1。
  • 注意这里没有用户1
  • @Strawberry 现在更新,监督。
  • 你还没有说,我们假设你使用的是支持递归查询的 MySQL 8.0 吗?

标签: mysql recursive-query


【解决方案1】:

对于 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;

【讨论】:

  • 让我找到一个支持这个的安装,我会试一试!
猜你喜欢
  • 2016-12-11
  • 2023-03-12
  • 2011-04-11
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 2020-07-29
  • 2019-03-09
  • 2015-05-05
相关资源
最近更新 更多