2011-12-09 24 views
5

这里的问题与我遇到的另一个问题有关...如何找到MySQL记录集中的ID差距?

我有数以百万计的记录,并且每个记录的ID都是自动递增的,不幸的是有时候生成的ID有时会被丢弃ID之间有很多差距。

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

什么是在MySQL中这样做的有效方法?

+0

相关:http://stackoverflow.com/questions/3718229/stop-mysql-reusing-auto-increment-ids –

+2

如果您使用INT作为主键,则可以拥有20亿条记录。为什么要努力填补空白?你用完了数字吗?我发现知道这些数字对应于添加记录的顺序是有好处的。 – minboost

+1

也许你会遇到性能较低的麻烦,通过将主键类型更改为BIGINT(如果INT提供的4个billon值太短),而不是尝试在非常大的表上重用ID。 –

回答

17

首先,您试图通过重复使用跳过的值来获得什么优势?一个普通的INT UNSIGNED会让你数到4,294,967,295。有了“数百万条记录”,在用完有效的ID之前,您的数据库将不得不增长1000倍以上。 (然后使用一个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为一个thourough和深思熟虑的答案 – qodeninja

+0

如果1是第一个差距它不会被退回 – morandi3

+0

在我的情况下,每个丢失的数字是重要的,所以是最后一段答案:) +1 Upvote – AamirR

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; 
+0

第二个查询''选择cs.n as bchain,ce.n as echain from cs,ce where cs.row = ce.row;''显示的联接实际上存在较大的差距,但第一个工作正常。 – magdmartin

1

该解决方案是更好的,如果你需要包括第一元素为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);