如何在 MySQL 记录集中找到 ID 间隙?

Posted

技术标签:

【中文标题】如何在 MySQL 记录集中找到 ID 间隙?【英文标题】:How can you find ID gaps in a MySQL recordset? 【发布时间】:2011-12-09 17:58:11 【问题描述】:

这里的问题与我的另一个问题有关......

我有数百万条记录,每条记录的 ID 都是自动递增的,不幸的是,有时生成的 ID 有时会被丢弃,因此 ID 之间有很多差距。

我想找到差距,并重新使用被放弃的 id。

mysql 中执行此操作的有效方法是什么?

【问题讨论】:

相关:***.com/questions/3718229/… 如果您使用 INT 作为主键,您可以拥有超过 20 亿条记录。为什么要努力填补空白?你的号码用完了吗?我发现知道数字与添加记录的顺序相对应是有好处的。 与尝试在非常大的表上重用 ID 相比,将主键类型更改为 BIGINT(如果 INT 提供的 4 十亿值太短)可能会减少性能问题。跨度> +1 以获得良好的反馈。我没有考虑过也许最好不要担心这些差距。 其他一些人在您有重用废弃 ID(在某些情况下属于死者的公民身份号码)的想法之前,这个“精明”的决定会给继承重用 ID 的人带来无穷无尽的问题.我不建议以任何方式做这样的事情。 【参考方案1】:

首先,您想通过重用跳过的值来获得什么优势?一个普通的INT UNSIGNED 可以让你数到 4,294,967,295。有了“数百万条记录”,您的数据库必须增长一千倍,然后才能用完有效的 ID。 (然后使用 BIGINT UNSIGNED 会增加 18,446,744,073,709,551,615 个值。)

尝试回收 MySQL 已跳过的值可能会占用您大量的时间来尝试补偿最初确实不会打扰 MySQL 的东西。

话虽如此,您可以通过以下方式找到丢失的 ID:

SELECT id + 1
FROM the_table
WHERE NOT EXISTS (SELECT 1 FROM the_table t2 WHERE t2.id = the_table.id + 1);

这只会在每个序列中找到第一个缺失的数字(例如,如果你有1, 2, 3, 8, 10,它会找到4,9)但它很可能是有效的,当然一旦你'填了一个ID就可以再次运行了。

【讨论】:

如果 1 是第一个间隙,则不会返回 在我的情况下,每个缺失的数字都很重要,答案的最后一段也很重要:) +1 Upvote【参考方案2】:

下面会为 mytab 中整数字段“n”中的每个间隙返回一行:

/* cs will contain 1 row for each contiguous sequence of integers in mytab.n
   and will have the start of that chain.
   ce will contain the end of that chain */
create temporary table cs (row int auto_increment primary key, n int);
create temporary table ce like cs;
insert into cs (n) select n from mytab where n-1 not in (select n from mytab) order by n;
insert into ce (n) select n from mytab where n+1 not in (select n from mytab) order by n;
select ce.n + 1 as bgap, cs.n - 1 as egap
  from cs, ce where cs.row = ce.row + 1;

如果您想要连续链而不是间隙,那么最终选择应该是:

select cs.n as bchain, ce.n as echain from cs,ce where cs.row=ce.row;

【讨论】:

第二个查询''select cs.n as bchain, ce.n as echain from cs,ce where cs.row=ce.row;''这个join就显示出实际存在的较大差距但是第一个效果很好。【参考方案3】:

此解决方案更好,以防您需要将第一个元素包含为 1:

SELECT
    1 AS gap_start,
    MIN(e.id) - 1 AS gap_end
FROM
    factura_entrada e
WHERE
    NOT EXISTS(
        SELECT
            1
        FROM
            factura_entrada
        WHERE
            id = 1
    )
LIMIT 1
UNION
    SELECT
        a.id + 1 AS gap_start,
        MIN(b.id)- 1 AS gap_end
    FROM
        factura_entrada AS a,
        factura_entrada AS b
    WHERE
        a.id < b.id
    GROUP BY
        a.id
    HAVING
        gap_start < MIN(b.id);

【讨论】:

【参考方案4】:

如果您使用的是MariaDB,您有更快的选择

SELECT * FROM seq_1_to_50000 where seq not in (select col from table);

文档:https://mariadb.com/kb/en/mariadb/sequence/

【讨论】:

以上是关于如何在 MySQL 记录集中找到 ID 间隙?的主要内容,如果未能解决你的问题,请参考以下文章

如果数据集中存在间隙,则使用mysql计算移动平均值会导致问题

MySQL有哪些行锁,是如何解决幻读的?

mysql间隙锁

mysql间隙锁

如何在 sqlite 表中找到间隙?

如何在 BigQuery 中找到每条记录(由 ID 标识)的所有不同值?