如何将许多行插入MySQL表并返回新ID?

时间:2011-09-07 11:58:29

标签: mysql bulkinsert

通常我可以在MySQL表中插入一行并获取last_insert_id。但是,现在,我想在表中批量插入许多行并返回一组ID。有谁知道我怎么能这样做?

有一些类似的问题,但它们并不完全相同。我不想将新ID插入任何临时表;我只是想取回一系列ID。

Can I retrieve the lastInsertId from a bulk insert?

Mysql mulitple row insert-select statement with last_insert_id()

9 个答案:

答案 0 :(得分:63)

旧线程,但刚刚调查过,所以这里说:如果你在最新版本的MySQL上使用InnoDB,你可以使用LAST_INSERT_ID()ROW_COUNT()获取ID列表。

InnoDB在进行批量插入时保证AUTO INCREMENT的序号,前提是innodb_autoinc_lock_mode设置为0(传统)或1(连续)。 因此,您可以通过添加LAST_INSERT_ID()来获取ROW_COUNT()-1第一个 ID和最后

答案 1 :(得分:13)

我能想到的唯一方法就是为每个插入的行存储一个唯一的标识符(guid) 然后选择行ID。 e.g:

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';

您还可以使用uuid()

在数据库中生成guid

答案 2 :(得分:3)

让我们假设我们有一个名为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;

答案 3 :(得分:0)

我认为您必须要么在应用程序中处理事务ID,要么在应用程序中处理项目ID,以便完美地完成此任务。

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

然后,您可以获取插入的id,其中包含受影响行数的循环,从lastid开始(这是批量插入的第一个插入ID)。 因此,我检查它完美的工作..请注意HeidiSQL例如不会返回ROW_COUNT()的正确值,可能是因为它是一个糟糕的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 - 假设您使用处理多个结果集/查询的扩展名。)

答案 4 :(得分:0)

我不确定自动增量值会将项目增加1。如果您的数据库具有“主控” //“主控”复制并解决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上也可以使用...

答案 5 :(得分: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。

答案 6 :(得分:0)

值得注意的是,@Dag Sondre Hansen 的答案也可以在您将 innodb_autoinc_lock_mode 设置为 2 的情况下通过在插入前简单地锁定表来实现。

LOCK TABLE my_table WRITE;
INSERT INTO my_table (col_a, col_b, col_c) VALUES (1,2,3), (4,5,6), (7,8,9);
SET @row_count = ROW_COUNT();
SET @last_insert_id = LAST_INSERT_ID();
UNLOCK TABLES;
SELECT id FROM my_table WHERE id >= @last_insert_id AND id <= @last_insert_id + (@row_count - 1);

这是一个小提琴演示:https://www.db-fiddle.com/f/ahXAhosYkkRmwqR9Y4mAsr/0

答案 7 :(得分:0)

如果您知道插入的行数,则可以通过另一种方式解决此问题。 例如,您插入了 4 行,表中的最后一个 ID 是 600

所以在执行插入查询后,您可以使用

select last_insert_id()

获取第一个插入 ID, 现在你可以做这个简单的计算了

last_insert_id() 值,此处为 601 所以,这将是 601 + 4 - 1 = 604

所以,601604 之间的所有 id

需要 InnoDB。确保将所有查询放入一个事务中。

编辑-
解决方案 2 仅当您有 varchar 或某种文本字段时才有效 将随机字符串添加到 varchar 或文本字段。 例如,随机字符串是 123sdfsklls113 你有 name 字段的值是 Sam,所以在这种情况下字段会变成

sam123sdfsklls113

将随机字符串附加到列值的末尾。

插入行后,现在您可以使用此轻松获取最后插入的行

select id from table_name where column_name like '%randomstring_here'

然后在获得 ids 后,您可以像这样轻松更新列值

update table_name set column_name = replace(column_name, 'randomstring_ here', '') where id in (last inserted ids here)

它将从列值中删除随机字符串。

答案 8 :(得分:-10)

$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);