如何在MySQL表中插入许多行并返回新的ID?


77

通常,我可以在MySQL表中插入一行并last_insert_id取回。现在,尽管如此,我想将许多行批量插入表中并获取ID数组。有人知道我该怎么做吗?

有一些类似的问题,但并不完全相同。我不想将新ID插入任何临时表;我只想找回ID数组。

我可以从批量插入中检索lastInsertId吗?

带有last_insert_id()的MySQL多行插入选择语句


为什么不能一一插入?
sanmai 2011年

您需要模拟该OUTPUT子句。我相信你可以在MySQL触发做到这一点
马丁·史密斯

您不能使用批量插入,然后再使用函数last_insert_id(),该方法将无法正常工作。
NB


4
否-该方法也存在缺陷-除非您将插入物包装在LOCK TABLES ... WRITE中,并且还应该允许auto_increment_increment
symcbean 2011年

Answers:


78

旧线程,但只是研究了一下,所以去了:如果您在MySQL的最新版本上使用InnoDB,则可以使用LAST_INSERT_ID()和获得ID列表ROW_COUNT()

如果innodb_autoinc_lock_mode设置为0(传统)或1(连续),InnoDB保证批量插入时自动递增的序号。因此,您可以添加添加第一个IDLAST_INSERT_ID()最后一个ID ROW_COUNT()-1


6
您需要交易吗?如果中间有其他插入物怎么办?

3
只要您使用InnoDB引擎并确保启用了auto_increment锁定(这可能会导致性能降低),就不需要显式的事务定义。由于InnoDB在插入期间执行锁定,因此其他插入必须等待插入完成。看看dev.mysql.com/doc/refman/5.1/en/...
达格松德雷汉森

1
MyISAM不是事务性数据库引擎,因此在进行大容量插入时可能会发生其他插入,从而弄乱了序列。锁定可能会阻止这种情况(虽然不太确定),但是我发现的一种解决方案(上述方法已由MySQL记录)已由MySQL记录。
Dag Sondre Hansen

1
@Wulf我假设(假定下划线)它是指INSERT,LOAD DATA,REPLACE INTO等
达格松德雷汉森

1
我认为还必须注意,如果您依靠这样的方法来获取多个插入ID,那么您将无法使用Galera Cluster这样的多主群集,如果您需要这样的话。
JohnK

17

我认为可以完成的唯一方法是,如果为插入的每组行(标识)存储唯一的标识符,然后选择行ID。例如:

INSERT INTO t1
(SELECT col1,col2,col3,'3aee88e2-a981-1027-a396-84f02afe7c70' FROM a_very_large_table);
COMMIT;

SELECT id FROM t1 
WHERE guid='3aee88e2-a981-1027-a396-84f02afe7c70';

您还可以通过使用以下命令在数据库中生成Guid uuid()


4

假设我们有一个名为temptable的表,其中有两个cols uid,col1,其中uid是一个自动递增字段。进行如下操作将返回结果集中所有插入的ID。您可以遍历结果集并获取ID。我意识到这是一个过时的帖子,并且此解决方案可能不适用于每种情况。但是对于其他人而言,这也许就是原因,所以我要回复它。

# lock the table
lock tables temptable write;

#bulk insert the rows;
insert into temptable(col1) values(1),(2),(3),(4);

#get the value of first inserted row. when bulk inserting last_insert_id() #should give the value of first inserted row from bulk op.
set @first_id = last_insert_id();

#now select the auto increment field whose value is greater than equal to #the first row. Remember since you have write lock on that table other #sessions can't write to it. This resultset should have all the inserted #id's
select uid from temptable where uid >=@first_id;

#now that you are done don't forget to unlock the table.
unlock tables;

0

我认为您将必须处理应用程序中的交易ID或应用程序中的项目ID,才能做到这一点。

假设您所有插入均成功(!),一种可行的方法如下:

然后,您可以使用循环获取受影响的行数的插入的ID,以循环数(从lastid(批量插入的第一个插入的ID)开始)开始。因此,我检查了它是否工作正常。.请注意,例如HeidiSQL不会为ROW_COUNT()返回正确的值,可能是因为它是一个cr脚的GUI,在执行随机操作时我们不要求这样做-但是从任何一个角度来看都是完全正确的命令行或PHP mysqli-

START TRANSACTION;
BEGIN;
INSERT into test (b) VALUES ('1'),('2'),('3');
SELECT LAST_INSERT_ID() AS lastid,ROW_COUNT() AS rowcount;
COMMIT;

在PHP中看起来像这样(local_sqle是对mysqli_query的直接调用,local_sqlec是对mysqli_query的调用+将结果集转换为PHP数组):

local_sqle("START TRANSACTION;
BEGIN;
INSERT into test (b) VALUES ('1'),('2'),('3');");
$r=local_sqlec("SELECT LAST_INSERT_ID() AS lastid,ROW_COUNT() AS rowcount;");
local_sqle("
COMMIT;");
$i=0;
echo "last id =".($r[0]['lastid'])."<br>";
echo "Row count =".($r[0]['rowcount'])."<br>";

while($i<$r[0]['rowcount']){
    echo "inserted id =".($r[0]['lastid']+$i)."<br>";
    $i++;
}

查询分开的原因是因为我无法使用我自己的函数来获得结果,如果您使用标准函数来执行此操作,则可以将其放回一个语句中,然后检索所需的结果(应该是结果编号2-假设您使用的扩展程序可以处理多个结果集/查询。


0

我不确定自动递增值将使项目增加1。如果您的数据库具有Master // Master复制并解决auto_increment重复排除项,将会出现很大的问题。AI将是+2而不是+1,而且如果还会有一个大师,它将变为+3。所以像AUTO_INCREMENT之类的东西接力1将杀死您的项目。

我只看到一些不错的选择。

该SQL代码段在使用多个主数据库时不会有任何问题,并且在只需要插入记录之前就可以提供良好的结果。在没有事务的情况下处理多个请求可以捕获其他插入记录。

START TRANSACTION;
SELECT max(id) into @maxLastId FROM `main_table`;
INSERT INTO `main_table` (`value`) VALUES ('first'), ('second') ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);
SELECT `id` FROM `main_table` WHERE id > @maxLastId OR @maxLastId IS NULL;
COMMIT;

(如果您还需要通过DUPLICATE KEY UPDATE来更新记录),则需要稍微重构数据库,SQL看起来将类似于下一个(对于事务是安全的,并且在一个连接中没有事务。)

#START TRANSACTION    
INSERT INTO bulk_inserts VALUES (null);
SET @blukTransactionId = LAST_INSERT_ID();
SELECT  @blukTransactionId, LAST_INSERT_ID();
INSERT INTO `main_table` (`value`, `transaction_id`) VALUES ('first', @blukTransactionId), ('second', @blukTransactionId) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), `transaction_id` = VALUES(`transaction_id`);
SELECT  @blukTransactionId, LAST_INSERT_ID();
SELECT id FROM `main_table` WHERE `transaction_id` = @blukTransactionId;
#COMMIT

两种情况都可以跨国。第一个将只显示您插入的记录,第二个将显示所有记录,甚至已更新。

这些选项即使在INSERT IGNORE下也可以使用...


0

这个线程很旧,但是所有这些解决方案都没有帮助我,所以我想出了自己的方法。

首先,计算要插入的行数

假设我们需要添加5行:

LOCK TABLE tbl WRITE;

SELECT `AUTO_INCREMENT` FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'my_db' AND TABLE_NAME   = 'tbl'

然后使用刚刚选择的auto_increment进行下一个查询:

ALTER TABLE tbl AUTO_INCREMENT = {AUTO_INCREMENT}+5;
UNLOCK TABLES;

最后插入

使用保留的自动增量范围插入ID。

警告:此解决方案需要提升对表的访问级别。但是通常,批量插入是由crons和importer脚本运行的,否则,它们可能仍会使用特殊的访问权限。您不会仅将其用于少量插入。

如果使用ON DUPLICATE KEY UPDATE,这可能会留下未使用的ID。


-11
$query = "INSERT INTO TABLE (ID,NAME,EMAIL) VALUES (NULL,VALUE1, VALUE2)";
$idArray = array();
foreach($array as $key) {
 mysql_query($query);
 array_push($idArray, mysql_insert_id());
}
print_r($idArray);

3
这不是批量插入查询。
和平
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.