2017-10-10 49 views
0

MySQL中的內置自動遞增不符合我的要求,所以我正在考慮製作一個新的。這裏是我的要求:如何讓我自己在PHP中自動增量?

  1. 創建增量序列號
  2. 能夠插入行(記錄)到失蹤人數。例如,我有5行,然後刪除第三行。之後,我插入另外2個新行。我希望其中一個將插入到第三排位置。

我的想法是使用循環來檢查表中的所有行。如果它發現一個缺失的位置,它會添加(a)新的行到缺失的位置。否則,它將繼續在表格末尾添加新行。

正如你所看到的,這個想法只適用於非常小的桌子。如果表擴展到,比方說,10MB。那麼服務器會遇到很大的麻煩。

我想知道是否有人有更好的算法,請賜教。

+0

這是一個可怕的想法。相反,**詢問一個新問題**,解釋「您的要求」是什麼,並尋求如何使用由專業人員創建的自動遞增ID來滿足它的幫助。創建「你自己的」是一個非起動器。 – alexis

回答

2

在自動增量列填補縫隙一般是沒有必要的,而且更麻煩比它值得。自動增量不是行號。它不需要是連續的,它只需要獨特的

如果你想填補空白,你會發現的問題之一是race condition。也就是說,在您的PHP腳本找到使用差距並插入該差距的毫秒之間,另一個PHP請求可能會做同樣的事情,找到相同的差距,然後填充它。

要解決這個問題,您的PHP腳本必須鎖定整個表,然後在之前搜索要使用的間隙。由於您正在尋找一個缺口,在沒有給定值存在行的情況下,您無法鎖定任何行。你必須鎖定整個表格,因爲在你找到差距之前你還不知道(如果有差距的話)。

使用表鎖定是一種代價昂貴的犧牲,因爲這意味着一次只能插入一個PHP請求。這成爲您應用程序可伸縮性的瓶頸。

現在談談您的實施。如何找到缺少的號碼?這些數字位於數據庫中,因此您可以查詢以查找ID不在表中的任何ID。

SELECT t1.id FROM mytable AS t1 
LEFT OUTER JOIN mytable AS t1 ON t2.id = t1.id - 1 
WHERE t2.id IS NULL 
ORDER BY t1.id 
LIMIT 1 

另一種方法是在應用程序中保留某種緩存的所有id。但是,這也意味着每個併發的PHP請求都需要訪問緩存,以及鎖定緩存的能力,以便一次只有一個PHP請求可以搜索並更新緩存。

無論採用哪種方式,您都爲您的應用程序創建了一個瓶頸。

我寫了更多關於這本書的第22章SQL Antipatterns: Avoiding the Pitfalls of Database Programming

+0

謝謝,但我不能想象如果id達到極限會發生什麼。 AI是否壓倒了桌子? –

+1

然後使用BIGINT。需要數千年來溢出BIGINT。看到我的答案https://stackoverflow.com/questions/24007583/integer-overflow-what-will-be-next/24007747#24007747 –

0

在實踐中,你必須非常小心,並且真的知道你在做什麼,因爲填充id列中的'空白'可能會破壞整個數據庫或系統的參照完整性,表被其他表引用。否則,一個快速的方法是首先使用mysql填充現有行的'空'id,例如,使用phpMyAdmin,用類似於以下,確保表以升序id列排序之後:

SET @count = 0; 
UPDATE the_table SET id = @count:= @count + 1; 
#Then after this you do your insert operations. 

以上將更新所有現有的IDS的順序,你插入的項目會只需將auto_incremented設置爲與表格中總行數相同的標識即可。

但是,如果你要離開已非空IDS完好無損,那麼你可以做這樣的事情在PHP中:

/* Assume that you want to insert into a table called the_table with columns id, col1, col2, col3 a new row with values for the three cols 
$value1, $value2, $value3 respectively, using an existing 'gap' in the id numbering: */ 

/* Get an array of all present ids: */ 
$arr = []; 
$q1 = mysqli_query($con,"SELECT id from the_table"); 
while(list($id) = mysqli_fetch_array($q1)){ 
$arr[] = $id; 
} 

/* Get the currently largest id in the table as $largest_id */ 
$q2 = mysqli_query($con,"SELECT MAX(id) from the_table"); 
list($largest_id) = mysqli_fetch_array($q2); 

/* Loop through all integers up to $largest_id + 1 */ 
/* And do the insert operation just one time, once you find a number not in $arr */ 
/* Use the $not_yet_inserted variable to break out of the loop */ 
$not_yet_inserted = true; 
for($j = 1; $j <= $largest_id + 1; $j += 1){ 
    if(!in_array($j,$arr) && $not_yet_inserted){ 
    mysqli_query($con,"INSERT INTO the_table (id, col1, col2,col3) values ('$j','$value1','$value3,'$value3'')"); 
    $not_yet_inserted = false; 
    } 
} 
0

您可以使用二進制搜索類型的算法。

首先獲取插入到數據庫中的所有ID。然後比較最大的id和列表的長度。如果兩者都相同,則插入具有下一個ID的行。如果沒有,則比較列表的一半處的ID和長度/ 2。現在,如果兩者相同,則意味着缺少的id在列表的前半部分之後,否則它在列表的前半部分。希望你能理解我想說的話。