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

发布于 2024-12-20 10:57:46 字数 155 浏览 2 评论 0原文

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

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

我想找到缺口,重新利用那些被废弃的id。

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

The issue here is related to another question I had...

I have millions of records, and the ID of each of those records is auto-incremented, unfortunately sometimes the ID that is generated is sometimes thrown away so there are many many gaps between IDs.

I want to find the gaps, and re-use the ids that were abandoned.

What's an efficient way to do this in MySQL?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

诠释孤独 2024-12-27 10:57:46

首先,您想通过重用跳过的值来获得什么优势?普通的 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,您就可以随时再次运行它。

First of all, what advantage are you trying to get by reusing the skipped values? An ordinary INT UNSIGNED will let you count up to 4,294,967,295. With "millions of records" your database would have to grow a thousand times over before running out of valid IDs. (And then using a BIGINT UNSIGNED will bump you up to 18,446,744,073,709,551,615 values.)

Trying to recycle values MySQL has skipped is likely to use up a lot of your time trying to compensate for something that really doesn't bother MySQL in the first place.

With that said, you can find missing IDs with something like:

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

This will find only the first missing number in each sequence (e.g., if you have {1, 2, 3, 8, 10} it will find {4,9}) but it's likely to be efficient, and of course once you've filled in an ID you can always run it again.

将军与妓 2024-12-27 10:57:46

以下将为 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;

The following will return a row for each gap in the integer field "n" in mytab:

/* 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;

If instead of the gaps you want the contiguous chains then the final select should be:

select cs.n as bchain, ce.n as echain from cs,ce where cs.row=ce.row;
无尽的现实 2024-12-27 10:57:46

如果您需要将第一个元素包含为 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);

This solution is better, in case you need to include the first element as 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);
残疾 2024-12-27 10:57:46

如果您使用的是 MariaDB,您有一个更快的选项

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

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

If you are using an MariaDB you have a faster option

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

docs: https://mariadb.com/kb/en/mariadb/sequence/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文