PDO - 插入多行数据时的lastInsertId()函数

11

我可以将2个宠物插入到表中,并逐个获取它们的lastInsertId()以进行进一步处理(2个查询)。
我想知道是否有一种方法,在插入2行数据时获取两个lastInsertIds()并将它们分配给变量:

我可以在一个查询中插入两行数据,但是只能逐个获取每个行的lastInsertId()。我想知道是否有办法同时获取这两个lastInsertIds()并分配给不同的变量。

$query = "INSERT INTO pets (pet_name) VALUES (':coco'),(':jojo')";
$pet_insert = $dbh->prepare($query);
$pet_insert->execute(array(':coco' => $coco,':jojo' => $jojo));
$New_PetID = $dbh->lastInsertId();

是否可以获取coco和jojo的lastInsertId()?就像这样:

$New_PetID1 = $dbh->lastInsertId();//coco
$New_PetID2 = $dbh->lastInsertId();//jojo

这将给出相同的ID,是否有任何方法可以获取这两个ID?仅供参考,此处位于try块中。

9个回答

16

1
可以通过获取last_insert id并将插入的行数(-1)相加来得到真正的最后一个id,是吗? - Peyman abdollahy
1
你读过这篇文章吗?http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html - Peyman abdollahy
@Peymanabdollahy,你有什么特别要说的吗?如果有,请明确说明。抱歉,我没有时间玩你的游戏。 - zerkms
该文章指出,对于每个语句,表都将被锁定,这可以保证每条记录按顺序保存。 - Peyman abdollahy
如果出现重复的宠物名称,INSERT IGNORE 会“跳过”这些 id。 - Rick James
显示剩余3条评论

9
你可以将1添加到上一个插入的ID中以实现真正的最后记录ID。如果你插入超过2个记录,只需将rowCount-1添加到last_insert_id即可。

当innodb_autoinc_lock_mode设置为0(“传统”)或1(“连续”)时,任何给定语句生成的自增值都将是连续的,没有间隙,因为表级AUTO-INC锁保持到语句结束,并且只有一个这样的语句可以执行。

并且

生成的ID在服务器上以每个连接为基础进行维护。这意味着函数返回给给定客户端的值是该客户端通过最近影响AUTO_INCREMENT列的语句生成的第一个AUTO_INCREMENT值。即使其他客户端生成自己的AUTO_INCREMENT值,此值也不会受到其他客户端的影响。此行为确保每个客户端可以检索其自己的ID,而不必担心其他客户端的活动,也不需要锁定或事务。

有关更多信息,请阅读此文档 http://dev.mysql.com/doc/refman/5.1/en/innodb-auto-increment-handling.html

这是答案,请阅读文章。为什么有负分? - Peyman abdollahy
如承诺 - 我会在24小时内在这里放置赏金(100,而不是50,因为50不可用)(SO现在不允许我这样做) - zerkms
@zerkms,我不建议使用这种方法。尝试使用插入语句:INSERT INTO test_table (id, name) VALUES (null, 'Example'), (15, 'Example'), (null, 'Example'); 这段代码对于这种情况不起作用。 - IStranger
2
@IStranger...确实,当显式插入auto_increment Id时,代码将无法工作。但是,如果您让mysql管理auto_increment id,这种方法将很好地工作。 - mcmurphy

3
请避免采用 Bruce 给出的解决方案。自增值 可以 被设置为与 1 不同的值。例如,在主到主复制的情况下。不久前,我们应用程序运行的主机更改了其数据库设置。我偶然发现,所有的 id 突然增加了两个而不是一个。如果我们有任何类似的代码,它可能会给我们造成严重的问题。

2

文档说明: 返回最后插入的行或序列值的ID

您需要执行两个查询以获取每个插入的行的ID。


1
我曾经认为这是不可能的,直到我自己尝试了一下,发现它是可以实现的

每次执行后,添加lastInsertId()并分配一个键。

以我的例子为例:

// First query
$sql = "INSERT INTO users
(username, email)
values
(:username, :email)";
$sth = $this->db->prepare($sql);
$sth->bindParam(':username', $data['username'], PDO::PARAM_STR);
$sth->bindParam(':email', $data['email'], PDO::PARAM_STR);

// Second Query
$sql2 = "INSERT INTO users
(username, email)
values
(:username, :email)";
$sth2 = $this->db->prepare($sql2);
$sth2->bindParam(':username', $data['username'], PDO::PARAM_STR);
$sth2->bindParam(':email', $data['email'], PDO::PARAM_STR);

// Start trans
$this->db->beginTransaction();

// Execute the two queries
$sth->execute();
$last['ux1'] = $this->db->lastInsertId(); // <---- FIRST KEY

$sth2->execute();
$last['ux2'] = $this->db->lastInsertId(); // <---- SECOND KEY

// Commit
$this->db->commit();

我能够检索到最后插入的两个ID

数组 ( [create] => 数组 ( [ux1] => 117 [ux2] => 118 )

)

希望这能帮助其他寻求正确答案的人。


1
你有两个预处理语句,而OP只有一个带有批量插入的查询。所以这并没有错,但它不符合OP的要求。 - zerkms
如果不是两个查询,而是100个查询,性能会怎样? - user2360831

1

好的,既然1000个插入比1个插入要长,那么主题问题仍然很有趣。可能的解决方法是进行两次查询。第一次查询是为了插入1000行,第二次查询是为了选择它们,如果在这些插入的行中有独特的内容。例如,对于宠物:

INSERT INTO pets ( name ) VALUES ( 'coco', 'jojo' );
SELECT id FROM pets WHERE name IN ( 'coco', 'jojo' );

这只对大数据集有益。

(您可能希望使用 SELECT name,id FROM ... 来确保它们的匹配方式。)(我的经验表明,批处理中超过100行的任何内容都比一次1行快大约10倍。) - Rick James

0

我在第一条语句中使用了数组,在第二条语句中添加了if条件。因此,如果插入记录获取其最后一个ID,则执行第二个语句。如果插入第二个数组获取其最后一个ID,则重复第二个语句。

    for ($count=0; $count < count($_POST["laptop_ram_capacity"]); $count++) { 
        $stmt = $con->prepare('INSERT INTO `ram` 
                (ram_capacity_id, ram_type_id, ram_bus_id, ram_brand_id, ram_module_id)
                VALUES (?,?,?,?,?)');

            //excute query//
            $stmt->execute(array(
                            $_POST['laptop_ram_capacity'][$count],
                            $_POST["laptop_ram_type"][$count],
                            $_POST["laptop_ram_bus"][$count],
                            $_POST["laptop_ram_brand"][$count],
                            $_POST["laptop_ram_module"][$count]
                            ));
            //fetsh the data//
            $rows = $stmt->rowCount();
            // i add the below statment in if condition to repeat the insert and pass the repeated of the last statement//
            if ($rows > 0) {
            $LASTRAM_ID = $con->lastInsertId();

            $stmt = $con->prepare('INSERT INTO `ram_devicedesc_rel` 
                (ram_id, Parent_device_description_id)
                VALUES (?,?)');

            //excute query//
            $stmt->execute(array(
                            $LASTRAM_ID,
                            $LASTdevicedesc_ID
                            ));
            //fetsh the data//
            } else {
                echo "Error: " . $sql . "<br>" . $conn->error;
            }                                    
        }

-1
你可以使用 'multi_query()' 将所有查询添加到一个变量中,例如: '$sql1 = "第一次插入";' '$sql2 = "第二次插入";' '$sql3 = "第三次插入";' 等等... 获取要插入的数量。 现在使用 'multi_query()' 执行所有 '$sql' 查询。 获取最后一个插入的id。 现在,你只需要 '$rowno = $lastinsertid - $countofinserts'。 基本上,你会得到一个数字。 加1并从结果编号到 lastinsertid 就是你运行插入查询的插入ID。

1
不要使用 multi_query,它存在安全隐患。 - Rick James

-3

这是可能的!您需要做的就是调用PDO lastInsertId()方法。这将在执行多个或批量INSERT时返回第一个插入的ID。接下来,我们需要对最后一个INSERT语句所影响的行数进行简单的加减操作。

$firstInsertedId = $this->lastInsertId();
$InsertedIds = range($firstInsertedId, ($firstInsertedId + ($stmt->rowCount() - 1)) );
print_r($InsertedIds);

我不建议在其中进行数学运算。最好的方法是每个插入单独进行。 - Louie Miranda
@RickJames我不建议使用这种方法。尝试使用插入语句:INSERT INTO test_table (id, name) VALUES (null, 'Example'), (15, 'Example'), (null, 'Example'); 这段代码在这种情况下无法工作。 - IStranger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接