通常我可以在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()
答案 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()
答案 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
所以,601
和 604
之间的所有 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);