2011-12-09 68 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);