MySQL中的内置自动增量不符合我的要求所以我正在考虑制作一个新的。这是我的要求:
我的想法是使用循环来检查表中的所有行。如果它找到了缺失的位置,它会将(a)新行添加到缺失的位置。否则,它将继续在表的末尾添加新行。
正如您所看到的,这个想法只适用于非常小的表格。如果表扩展到,比方说10MB。然后服务器将遇到很大麻烦。
我想知道是否有人有更好的算法,请赐教。
答案 0 :(得分: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中写了更多关于此的内容。
答案 1 :(得分:0)
在实践中,你必须非常小心并且真的知道你在做什么,因为填充id列中的'gap'会破坏整个数据库或系统的参照完整性,其中id在那个表中由其他表引用。
否则,快速的方法是首先使用mysql按现有行填充那些'空'ID,例如使用phpmyadmin,使用类似下面的内容,确保表在id列上按升序排序:
SET @count = 0;
UPDATE the_table SET id = @count:= @count + 1;
#Then after this you do your insert operations.
以上内容会将所有现有ID更新为连续顺序,您插入的项目将被auto_incremented一个id等于表中的总行数。
但是如果你想保留已经非空的id,那么你可以在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;
}
}
答案 2 :(得分:0)
您可以使用二分搜索算法。
首先获取在数据库中插入的所有ID。然后将最大的id与列表的长度进行比较。如果两者都相同,则插入具有下一个id的行。如果没有,则比较列表一半的id和 length / 2 。现在,如果两者都相同,则意味着缺少的id位于列表的前半部分之后,否则它位于列表的前半部分。希望你能理解我想说的话。