【PDO擴展】lastInsertId函數返回0的原因

【PDO擴展】lastInsertId函數返回0的原因

問題

在使用PHP的PDO擴展插入數據的時候,有時候需要獲取到最後插入記錄的ID作爲返回信息。要怎麼才能實現這個需求呢?

lastInsertId函數

使用PDO的lastInsertId函數。

但是,最近在使用的過程中發現有時候lastInsertId函數返回的是0。爲什麼會這樣呢?

先來看看lastInsertId函數在PHP手冊上的說明。

返回最後插入行的ID或序列值。

再來看看下面的幾個例子。

測試例子

主鍵是ID字段,使用自增約束。

測試數據庫表建表語句1

<?php
    $dsn = 'mysql:dbname=test;host=127.0.0.1';
    $user = 'root';
    $password = 'root';

    try{
            $dbh = new PDO($dsn, $user, $password);
    }catch(PDOException $e){
            echo "Connection failed: " . $e->getMessage();
    }

    for( $i = 0; $i < 10; $i++){
            $sql = 'INSERT INTO `tbl_test` (id, name) VALUE (:id, :name)';
            $data = array(
                    ':id' => '',
                    ':name' => "user_$i"
            );
            $sth = $dbh->prepare($sql);
            $sth-&gt;execute($data);
    }

    $sql = 'INSERT INTO `tbl_test` (id, name) VALUE (:id, :name)';
    $new_data = array(
            ':id' => '',
            ':name' => 'user_new'
    );
    $sth = $dbh->prepare($sql);
    $sth-&gt;execute($new_data);
    $last_id = $dbh->lastInsertId();
    echo 'last id: ' . $last_id;

結果

last id: 11

主鍵是ID字段,不使用自增約束。

測試數據庫表建表語句2

<?php
    $dsn = 'mysql:dbname=test;host=127.0.0.1';
    $user = 'root';
    $password = 'root';

    try{
            $dbh = new PDO($dsn, $user, $password);
    }catch(PDOException $e){
            echo "Connection failed: " . $e->getMessage();
    }

    for( $i = 0; $i < 10; $i++){
            $sql = 'INSERT INTO `tbl_test` (id, name) VALUE (:id, :name)';
            $data = array(
                    ':id' => $i,
                    ':name' => "user_$i"
            );
            $sth = $dbh->prepare($sql);
            $sth-&gt;execute($data);
    }

    $sql = 'INSERT INTO `tbl_test` (id, name) VALUE (:id, :name)';
    $new_data = array(
            ':id' => '',
            ':name' => 'user_new'
    );
    $sth = $dbh->prepare($sql);
    $sth-&gt;execute($new_data);
    $last_id = $dbh->lastInsertId();
    echo 'last id: ' . $last_id;

結果

last id: 0

主鍵不是ID字段,主鍵使用自增約束。

測試數據庫表建表語句3

<?php
    $dsn = 'mysql:dbname=test;host=127.0.0.1';
    $user = 'root';
    $password = 'root';

    try{
            $dbh = new PDO($dsn, $user, $password);
    }catch(PDOException $e){
            echo "Connection failed: " . $e->getMessage();
    }

    for( $i = 0; $i < 10; $i++){
            $sql = 'INSERT INTO `tbl_test` (tbl_id, name) VALUE (:tbl_id, :name)';
            $data = array(
                    ':tbl_id' => $i,
                    ':name' => "user_$i"
            );
            $sth = $dbh->prepare($sql);
            $sth-&gt;execute($data);
    }

    $sql = 'INSERT INTO `tbl_test` (tbl_id, name) VALUE (:tbl_id, :name)';
    $new_data = array(
            ':tbl_id' => '',
            ':name' => 'user_new'
    );
    $sth = $dbh->prepare($sql);
    $sth-&gt;execute($new_data);
    $last_id = $dbh->lastInsertId();
    echo 'last id: ' . $last_id;

結果

last id: 11

主鍵不是ID字段,不使用自增約束。

測試數據庫表建表語句4

<?php
    $dsn = 'mysql:dbname=test;host=127.0.0.1';
    $user = 'root';
    $password = 'root';

    try{
            $dbh = new PDO($dsn, $user, $password);
    }catch(PDOException $e){
            echo "Connection failed: " . $e->getMessage();
    }

    for( $i = 0; $i < 10; $i++){
            $sql = 'INSERT INTO `tbl_test` (tbl_id, name) VALUE (:tbl_id, :name)';
            $data = array(
                    ':tbl_id' => uniqid(),
                    ':name' => "user_$i"
            );
            $sth = $dbh->prepare($sql);
            $sth-&gt;execute($data);
    }

    $sql = 'INSERT INTO `tbl_test` (tbl_id, name) VALUE (:tbl_id, :name)';
    $new_data = array(
            ':tbl_id' => uniqid(),
            ':name' => 'user_new'
    );
    $sth = $dbh->prepare($sql);
    $sth-&gt;execute($new_data);
    $last_id = $dbh->lastInsertId();
    echo 'last id: ' . $last_id;

結果

last id: 0

查看PHP源碼

可以看到,有些例子返回0,有些例子返回最新的ID。那麼lastInsertId什麼情況下會返回0呢?在網上搜了很多資料,並沒有發現想要的答案,翻開PHP源碼,發現函數last_insert_id的實現源碼是這樣的:

函數last_insert_id源碼

可以看到,函數返回的id的值是調用mysql api中的mysql_insert_id函數返回的值。

查看mysql手冊

翻開mysql手冊,在這裏找到這一段:

mysql_insert_id() returns the value stored into an AUTO_INCREMENT column, whether that value is automatically generated by storing NULL or 0 or was specified as an explicit value. LAST_INSERT_ID() returns only automatically generated AUTO_INCREMENT values. If you store an explicit value other than NULL or 0, it does not affect the value returned by LAST_INSERT_ID().

結論

從手冊的描述可以知道,mysql_insert_id函數返回的是儲存在有AUTO_INCREMENT約束的字段的值,如果表中的字段不使用AUTO_INCREMENT約束或者使用自己生成的唯一值插入,那麼該函數不會返回你所存儲的值,而是返回NULL或0。因此,在沒有使用AUTO_INCREMENT約束的表中,或者ID是自己生成的唯一ID,lastInsertId函數返回的都是0。

解決方案

那麼,有沒有另一種方法可以幫助我們判斷程序執行插入是否成功呢?答案是有的。在PDO執行了excecute之後,調用PDO實例的rowCount函數可以得到執行之後的影響行數,如果結果非0,那麼說明數據庫插入操作執行成功了。下面這個解決方案的一小段demo:

$sql = 'INSERT INTO `tbl_test` (tbl_id, name) VALUE (:tbl_id, :name)';
$new_data = array(
        ':tbl_id' => uniqid(),
        ':name' => 'user_another'
);
$sth = $dbh->prepare($sql);
$sth-&gt;execute($new_data);
$row_count = $sth->rowCount();
if( $row_count ){
    echo 'execute success';
} else{
    echo 'execute failed';
}

本文探討一個問題出現的原因和一個解決方案,由於個人水平有限,如有更好的方法或者其他建議和批評,歡迎指出。

注:本文使用的是PHP5.4.15,MySQL5.5.41。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章