结果可以由 MySQL 查询返回,无需脚本。
SELECT CONCAT(LPAD(r.lo,4,'0'),'-',LPAD(r.hi,4,'0')) AS gap
, r.lo
, r.hi
-- , d.minval IS NULL AS gap
-- , d.*
FROM ( SELECT rl.lo, rh.hi
FROM (SELECT 0000 AS lo UNION
SELECT rlo.maxval+1
FROM example1 rlo
WHERE rlo.maxval < 9999
) rl
JOIN (SELECT 9999 AS hi UNION
SELECT rhi.minval-1
FROM example1 rhi
WHERE rhi.minval > 0000
) rh
ON rh.hi >= rl.lo
GROUP BY rl.lo, rh.hi
) r
LEFT
JOIN example1 d
ON r.lo BETWEEN d.minval+0 AND d.maxval+0
OR r.hi BETWEEN d.minval+0 AND d.maxval+0
OR d.minval+0 BETWEEN r.lo AND r.hi
OR d.maxval+0 BETWEEN r.lo AND r.hi
WHERE d.minval IS NULL
ORDER
BY r.lo, r.hi
-- , d.minval, d.maxval
我使用的方法是从一组所有可能的差距开始。我们知道,每一个潜在的差距都会:
- 开始于
0000
或任何maxval+1
- 结束于
9999
或任何minval-1
因此,我们可以生成所有可能的“间隙开始”的列表和所有可能的“间隙结束”的列表。(当我编写查询时,我将这些视为“范围”。我为内联视图、r
“范围”、范围rh
的“高端”和范围rl
的“低”端使用了别名。
分配了别名 " " 的内联视图r
返回所有可能存在间隙的行。(这几乎是一个交叉连接,但我们消除了 r.hi 小于 r.lo 的行。我认为用于此的实际术语是“不等式连接”。)
回到原始范围表的反连接模式消除了不是真正间隙的行,因为与表中的一个范围有一些重叠。(反连接模式是一个 LEFT JOIN,然后是 WHERE 子句中的谓词来消除行,如果 LEFT JOIN 操作找到匹配项,所以我们留下没有匹配的行。作为替代方案,相同可以使用一种NOT EXISTS (correlated subquery)
方法来完成消除。)
(此查询还将返回0000-9999
范围表中没有行的间隙。)
设置测试用例(我调整了 OP0000
值以0055
证明这将识别一个以 . 开头的间隙0000
。)
CREATE TABLE `example1` (minval INT(4), maxval INT(4));
INSERT INTO `example1` VALUES (0055,1000),(1500,8999),(0100,0200),(5000,6999);
gap lo hi
--------- ----- -----
0000-0054 0 54
1001-1499 1001 1499
9000-9999 9000 9999
编辑
我刚刚注意到数据类型是 CHAR(4) 的 OP 注释,我假设它是整数类型,可能INT(4) ZEROFILL
。上面的查询也适用于 CHAR 类型,但我们需要确保将 CHAR 转换为整数,最简单的方法是在列引用中添加“+0”,进行调整并进行测试。
CREATE TABLE `example1` (minval CHAR(4), maxval CHAR(4));
INSERT INTO `example1` VALUES ('0055','1000')
,('1500','8999'),('0100','0200'),('5000','6999');