请记住,如果您使用事务,则应在提交之前使用 lastInsertId
否则它将返回 0
(PHP 5 >= 5.1.0,PHP 7,PHP 8,PECL pdo >= 0.1.0)
PDO::lastInsertId — 返回最后插入行的 ID 或序列值
返回最后插入行的 ID,或者根据底层驱动程序返回序列对象的最后一个值。例如,PDO_PGSQL 允许为 name
参数指定任何序列对象的名称。
注意:
此方法可能无法在不同的 PDO 驱动程序之间返回有意义或一致的结果,因为底层数据库甚至可能不支持自动递增字段或序列的概念。
name
应从中返回 ID 的序列对象的名称。
如果没有为 name
参数指定序列名称,则 PDO::lastInsertId() 返回一个字符串,表示插入到数据库中的最后一行 的行 ID。
如果为 name
参数指定了序列名称,则 PDO::lastInsertId() 返回一个字符串,表示从指定的序列对象中检索到的最后一个值。
如果 PDO 驱动程序不支持此功能,则 PDO::lastInsertId() 将触发 IM001
SQLSTATE。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_WARNING
,则发出级别为 E_WARNING
的错误。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_EXCEPTION
,则抛出 PDOException。
为了节省大家的时间。
在使用 MySQL 或 MariaDB 时,如果在单个查询中插入多行(INSERT INTO table (a,b,c) VALUES (1,2,3), (2,3,4), ...)到具有 auto_increment 列的表中,PDO::lastInsertId 不会返回最后一行 的自动生成的 id。相反,它返回第一个生成的 id。这可以通过查看 MySQL 和 MariaDB 的文档来解释。
引用自各自的文档,
MySQL
"如果没有参数,LAST_INSERT_ID() 返回一个 BIGINT UNSIGNED(64 位)值,表示作为最近执行的 INSERT 语句的结果,成功插入的 AUTO_INCREMENT 列的第一个自动生成的值。"
MariaDB
"LAST_INSERT_ID()(无参数)返回作为最近执行的 INSERT 语句的结果,成功插入的 AUTO_INCREMENT 列的第一个自动生成的值。"
这显然与 lastInsertId 自身文档中所述内容不符。希望这可以帮助某些人避免调试 ID 不匹配的原因。
tl;dr (MySQL | Mariadb) + 多行插入 + PDO::lastInsertId = 第一个自动生成的 id
在运行 Windows 7 的 MariaDB 10.2.6 32 位、PHP 5.6.31 32 位和 mysqlnd 5.0.11 上测试的行为。
在 mysql 中使用事务时,请注意 lastInsertId()。以下代码返回 0 而不是插入 id。
<?php
try {
$dbh = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
$stmt = $dbh->prepare("INSERT INTO test (name, email) VALUES(?,?)");
try {
$dbh->beginTransaction();
$tmt->execute( array('user', '[email protected]'));
$dbh->commit();
print $dbh->lastInsertId();
} catch(PDOExecption $e) {
$dbh->rollback();
print "Error!: " . $e->getMessage() . "</br>";
}
} catch( PDOExecption $e ) {
print "Error!: " . $e->getMessage() . "</br>";
}
?>
如果没有抛出异常,lastInsertId 将返回 0。但是,如果在调用 commit 之前调用 lastInsertId,则会返回正确的 id。
以防有人想知道
类似于
$val = 5;
$sql = "REPLACE table (column) VALUES (:val)";
$stmt = $dbh->prepare($sql);
$stmt->bindParam(':val', $val, PDO::PARAM_INT);
$stmt->execute();
$lastId = $dbh->lastInsertId();
将返回最后插入的 id,无论记录是被替换还是仅仅被插入
REPLACE 语法,简单插入,或删除 > 插入
所以 lastInsertId() 仍然有效
请参阅 https://mysqlserver.cn/doc/refman/5.0/en/replace.html
有关 REPLACE 用法
如果您通过 FreeTDS 从 Linux 访问 MSSQL/SQL Server 2008 R2(或更高版本),则获取最后插入 ID 的方法比下面概述的解决方案稍微简洁一些。
此处概述了相关的特定 SQL
http://msdn.microsoft.com/en-us/library/ms177564.aspx
例如,对于包含两列(product_id、product_name)的表,其中 product_id 是 uniqueidentifier 或类似的东西,您可以执行以下操作。
<?php
// 假设 $dbh 连接句柄已建立
$sql = "INSERT INTO product (product_name) OUTPUT INSERTED.product_id VALUES (?)";
$sth = $dbh->prepare($sql);
$sth->execute(array('widgets'));
$temp = $sth->fetch(PDO::FETCH_ASSOC);
?>
然后 $temp 将包含一个类似以下的数组
数组
(
[product_id] => E1DA1CB0-676A-4CD9-A22C-90C9D4E81914
)
需要注意的是,根据您选择的 TDS 版本,PDO_DBLIB/FreeTDS 在处理 uniqueidentifier 列方面存在一些问题,这些问题直到 PHP 5.3.7 才得到修复。
有关此问题和补丁的信息,请访问以下链接
https://bugs.php.net/bug.php?id=54167
需要说明的是,此函数**不**检索行的 ID(主键),而是检索其 OID。
因此,如果您使用的是最新版本的 PostgreSQL,除非您在创建表时专门向其中添加了 OID,否则此函数将无法帮助您。
我认为在 Postgres 中,可以使用从 Postgres 8.2 版本开始提供的 RETURNING 来获取 ID,这是一个不错的解决方案。在下面的示例中,我在 insert 语句中添加了 "returning" 以及表的主键,然后在执行后,我执行了一个 fetch 操作,获取了一个包含最后插入 ID 值的数组。
<?php
public function insert($employee){
$sqlQuery = "INSERT INTO employee(user_id,name,address,city) VALUES(:user_id,:name,:address,:city) RETURNING employee_id";
$statement = $this->prepare($sqlQuery);
$a ="2002-03-11 12:01AM" ;
$statement->bindParam(":user_id", $employee->getUserId(), PDO::PARAM_INT);
$statement->bindParam(":name", $employee->getName(), PDO::PARAM_STR);
$statement->bindParam(":address", $employee->getAddress(), PDO::PARAM_STR);
$statement->bindParam(":city", $employee->getCity(), PDO::PARAM_STR);
$statement->execute();
$result = $statement->fetch(PDO::FETCH_ASSOC);
return $result["employee_id"];
}
?>
**警告:针对 PostgreSQL 用户!** 针对 ed at hicklinslade dot com 的评论,他写道
...
$last_insert_id = $objPDO->lastInsertId("$strTable_id_seq);
此函数似乎按预期工作。我不太清楚的是,它是否只是返回序列的当前值;如果是这样,这并不是您代码刚刚插入的记录 ID 的可靠指标,尤其是在您的网站或应用程序流量非常大的情况下。
...
**切勿**在 PostgreSQL 序列中使用 lastInsertId(),**尤其是在应用程序的插入/更新负载很高时**。PostgreSQL 序列是非事务性的(一种自然的设计特性,用于避免排他性锁定,否则会导致无法接受的性能)。这意味着任何并发事务递增同一序列都会使 lastInsertId() 返回的值相对于您事务的最后一次插入无效。例如
事务 1 使用 nextval('some_seq') 插入,产生 100;
并发事务 2 使用 nextval('some_seq') 插入,产生 101;
事务 1 调用 lastInsertId(),期望得到 100,**但得到 101**。
此 PDO 方法对于 PostgreSQL 来说是愚蠢的,始终使用 INSERT ... RETURNING 代替。此致。
$dbh->commit();
print $dbh->lastInsertId();
以上代码将始终返回零 (0)
因此,在提交事务之前调用 $dbh->lastInsertId(); 非常重要
以上代码应修改为
print $dbh->lastInsertId();
$dbh->commit();
此函数现在与更新的 MS SQL 驱动程序兼容。http://msdn.microsoft.com/en-us/library/ff628155(v=sql.105)
MySQL/MariaDB 用户请注意,尽管此函数返回字符串,但如果您的列具有 ZEROFILL 属性,则**不会保留前导零**。
关于通过类创建的连接
例如:db::SQL()->query();
然后 db::SQL()->lastInsertId();
它将创建一个新的连接,并且不会返回插入的最后一个 ID。最好包含一个 PDO 连接文件(或直接包含登录信息)并使用它来正确获取最后一个 ID。
$db = new PDO(logins);
$db->query();
$db->lastInsertId();