作为一名强迫症患者,看到文章 ID 不连续简直抓狂!本文记录了我如何用 SQL 找回那些"消失"的 ID。
我有一个运行多年的 Z-Blog 博客,累计发布了十几万篇文章。多年来,删删改改,导致主键 ID 变得支离破碎:
更夸张的是,最大 ID 已经到了 14,692,488,但实际只有 13,500,210 条记录。这意味着有 约 119 万个 ID 被浪费了!毕竟我是一个爬虫爱好者。哈哈
作为一个强迫症,我决定要把这些 ID 找回来,让它们重新"上岗"。
Z-Blog 的文章表是 zbp_post,主键字段是 log_ID。
zbp_delete 表中经过一番研究,我找到了一个高效的 SQL 方案,核心思路是:
-- ============================================
-- Z-Blog 主键 ID 复用方案
-- 适用于 MySQL 5.x(不支持递归CTE的版本)
-- ============================================
-- 第一步:创建数字辅助表
DROP TABLE IF EXISTS numbers;
CREATE TABLE numbers (n INT PRIMARY KEY);
-- 第二步:创建存储过程,填充数字表
DELIMITER $$
CREATE PROCEDURE fill_numbers(IN max_num INT)
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < max_num DO
INSERT IGNORE INTO numbers VALUES (i+1);
SET i = i + 1000;
INSERT IGNORE INTO numbers VALUES
(i),(i+1),(i+2),(i+3),(i+4),(i+5),(i+6),(i+7),(i+8),(i+9),
(i+10),(i+11),(i+12),(i+13),(i+14),(i+15),(i+16),(i+17),(i+18),(i+19),
(i+20),(i+21),(i+22),(i+23),(i+24),(i+25),(i+26),(i+27),(i+28),(i+29),
(i+30),(i+31),(i+32),(i+33),(i+34),(i+35),(i+36),(i+37),(i+38),(i+39),
(i+40),(i+41),(i+42),(i+43),(i+44),(i+45),(i+46),(i+47),(i+48),(i+49),
(i+50),(i+51),(i+52),(i+53),(i+54),(i+55),(i+56),(i+57),(i+58),(i+59),
(i+60),(i+61),(i+62),(i+63),(i+64),(i+65),(i+66),(i+67),(i+68),(i+69),
(i+70),(i+71),(i+72),(i+73),(i+74),(i+75),(i+76),(i+77),(i+78),(i+79),
(i+80),(i+81),(i+82),(i+83),(i+84),(i+85),(i+86),(i+87),(i+88),(i+89),
(i+90),(i+91),(i+92),(i+93),(i+94),(i+95),(i+96),(i+97),(i+98),(i+99);
SET i = i + 900;
END WHILE;
END$$
DELIMITER ;
-- 第三步:填充数字表(替换为你的最大ID)
-- 先查一下最大ID:SELECT MAX(log_ID) FROM zbp_post;
CALL fill_numbers(15000000);
-- 第四步:创建可复用ID存储表
DROP TABLE IF EXISTS zbp_delete;
CREATE TABLE zbp_delete (
id INT PRIMARY KEY,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 第五步:用 LEFT JOIN 找出所有缺失的ID(最关键的一步!)
INSERT INTO zbp_delete (id)
SELECT n.n
FROM numbers n
LEFT JOIN zbp_post p ON n.n = p.log_ID
WHERE p.log_ID IS NULL;
-- 第六步:查看结果
SELECT COUNT(*) AS '可复用ID数量' FROM zbp_delete;
SELECT * FROM zbp_delete ORDER BY id LIMIT 100;可复用ID数量: 1192278成功找回了 119 万多个可复用的 ID!
操作 | 耗时 |
|---|---|
创建并填充 1500 万数字表 | 约 3 分钟 |
LEFT JOIN 找出缺失 ID | 约 2 分钟 |
总计 | 约 5 分钟 |
对比之前用代码逐个检查的方案(预计需要几小时),效率提升了 几十倍!
NOT IN 子查询快得多递归 CTE(Common Table Expression)是 MySQL 8.0 引入的功能,语法优雅:
-- MySQL 8.0+ 递归CTE方案(更简洁,但不兼容低版本)
WITH RECURSIVE number_sequence AS (
SELECT 1 AS id
UNION ALL
SELECT id + 1 FROM number_sequence
WHERE id < (SELECT MAX(log_ID) FROM zbp_post)
)
SELECT id FROM number_sequence
WHERE id NOT IN (SELECT log_ID FROM zbp_post);但我的服务器还在用 MySQL 5.7,只能用数字辅助表的方案。
有了 zbp_delete 表,以后插入新文章时就可以优先使用这些"回收"的 ID:
-- 获取一个可复用的ID
SELECT id FROM zbp_delete ORDER BY id LIMIT 1;
-- 插入文章后,删除该ID
DELETE FROM zbp_delete WHERE id = ?;或者在 Z-Blog 代码中修改插入逻辑,实现自动复用。
作为一个强迫症患者,看到 ID 终于可以连续了,心里舒服多了!
这次优化的关键收获:
LEFT JOIN ... IS NULL 比 NOT IN 性能好得多希望这个方案能帮到同样有强迫症的博主们!😄
本文 SQL 已在我的 Z-Blog 博客上验证通过,MySQL 版本 5.7.40
如果有更好的方案,欢迎留言讨论!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。