2017-10-10 48 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在列表的前半部分之后,否则它在列表的前半部分。希望你能理解我想说的话。