請記住,如果您使用交易,則應在提交**之前**使用 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。
如果屬性 E_WARNING
設定為 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 不符的原因。
簡而言之:(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();
$stmt->execute( array('user', 'user@example.com'));
$dbh->commit();
print $dbh->lastInsertId();
} catch(PDOException $e) {
$dbh->rollback();
print "錯誤!: " . $e->getMessage() . "</br>";
}
} catch( PDOException $e ) {
print "錯誤!: " . $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://mysql.dev.org.tw/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。
對於 PostgreSQL 來說,這種 PDO 方法很笨,請務必使用 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();