PHP中的MySQLi擴充套件學習(三)mysqli的基本操作

highhand發表於2021-09-09

我們繼續 MySQLi 擴充套件的學習,上篇文章中提到過,MySQLi 的擴充套件相對於 PDO 來說功能更加的豐富,所以我們依然還會在學習過程中穿插各種 MySQLi 中好玩的方法函式。不過,今天的主角是 MySQLi 中如何執行 SQL 語句以及多條 SQL 語句的執行。

連線與選擇資料庫

首先是一個小內容的學習分享,依然還是連線資料庫,不過這次我們用另外一種方式來進行連線。

$mysqli = new mysqli();
$mysqli->real_connect("localhost", "root", "", "blog_test");

var_dump($mysqli); 
// ["thread_id"]=>
// int(163)

$mysqli->real_connect("localhost", "root2", "123", "blog_test");
var_dump($mysqli);
// ["thread_id"]=>
// int(164)

首先,我們例項化了一個 mysqli 物件。在例項化過程中,我們並沒有給 mysqli 的建構函式傳遞任何的引數,而是使用 real_connect() 方法來傳遞資料庫伺服器資訊並建立連線。

相信不少朋友從程式碼中就可以看出,我們使用 real_connect() 可以在一個 mysqli 例項下來切換不同的資料庫連線。透過列印 mysqli 物件的內容就可以看出,兩個連線的執行緒ID不同,也就是說,它們是不同的兩個連線,但是使用的都是最上面所初始化的那個 mysqli 物件。

連線可以切換了,那麼我們要連線的資料庫呢?當然也可以方便地切換。

$mysqli->select_db('mysql');

就是這樣一個簡單的 select_db() 方法,就可以幫助我們在程式碼執行過程中動態地修改所連線的資料庫。

執行 SQL 語句

對於 PDO 來說,如果是查詢語句,我們需要使用 query() 方法,如果是增、刪、改之類的其它語句,我們要使用 exec() ,透過這兩個方法分別執行不同的 SQL 語句。但是在 MySQLi 中,我們統一隻使用 query() 方法就可以了。

$mysqli->query("insert into zyblog_test_user(username, password, salt) values('3a', '3a', '3a')");
var_dump($mysqli->affected_rows);
var_dump($mysqli->insert_id);

$mysqli->query("update zyblog_test_user set password='3aa' where username='3a'");
var_dump($mysqli->affected_rows);

$mysqli->query("delete from zyblog_test_user where id = 60");
var_dump($mysqli->affected_rows);

$res = $mysqli->query("select * from zyblog_test_user where username='3a'");
print_r($res);
// mysqli_result Object
// (
//     [current_field] => 0
//     [field_count] => 4
//     [lengths] =>
//     [num_rows] => 3
//     [type] => 0
// )

print_r($res->fetch_assoc());
// Array
// (
//     [id] => 61
//     [username] => 3a
//     [password] => 3aa
//     [salt] => 3a
// )

while ($row = $res->fetch_assoc()) {
    print_r($row);
}
// Array
// (
//     [id] => 62
//     [username] => 3a
//     [password] => 3aa
//     [salt] => 3a
// )
// Array
// (
//     [id] => 63
//     [username] => 3a
//     [password] => 3aa
//     [salt] => 3a
// )
// ……

對於增、刪、改之類的語句,query() 方法只會返回一個布林值,也就是語句是否執行成功。記住,它返回的不是受影響的行數,這一點是需要注意的。我們如果需要獲取受影響的行數需要使用 MySQLi 的屬性 affected_rows 。對於插入語句來說,獲取最新插入的資料ID使用的是 insert_id 屬性。

如果執行的是 SELECT 語句,那麼 query() 返回的就是一個 mysqli_result 物件,它代表從一個資料庫查詢中獲取的結果集。關於這個物件的內容我們將在後面的文章中進行詳細的說明。

執行多條 SQL 語句

執行多條 SQL 語句的能力對於 PDO 來說是無法實現的,不過據說 PDO 是支援的,語句是可以正常執行的,但是我們拿不到完整的返回結果。

$sql = "insert into zyblog_test_user(username, password, salt) values('3bb', '3bb', '3bb');"
        . "update zyblog_test_user set password='3aa' where username='3a';"
        . "select * from zyblog_test_user where username='3b';"
        . "select now()";

$pdo = new PDO("mysql:dns=locahost;dbname=blog_test", 'root', '', [PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);
$res = $pdo->exec($sql);
var_dump($res); // int(1)
$stmt = $pdo->query($sql);
foreach ($stmt as $row) { //PHP Fatal error:  Uncaught PDOException: SQLSTATE[HY000]: General error in
    var_dump($row);
}

從程式碼中可以看出,如果使用的是 exec() 方法,那麼返回的就是 INSERT 語句的結果。如果使用 query() 方法,返回的雖然是 PDOStatement 物件,但是它是無法遍歷的。

接下來我們就看看 MySQLi 是如何來執行這個多條語句拼接在一起的 SQL 語句的。

$mysqli->multi_query($sql);
$i = 1;
do{
    echo '第' . $i . '條:', PHP_EOL;
    $i++;
    $result = $mysqli->use_result();
    var_dump($result);
    var_dump($mysqli->affected_rows);
    if(is_object($result)){
        var_dump($result->fetch_assoc());
    }
    var_dump($mysqli->next_result());
    echo '========', PHP_EOL;
} while($mysqli->more_results() );
// 第1條:
// bool(false)
// int(1)
// ========
// 第2條:
// bool(false)
// int(0)
// ========
// 第3條:
// object(mysqli_result)#2 (5) {
//   ["current_field"]=>
//   int(0)
//   ["field_count"]=>
//   int(4)
//   ["lengths"]=>
//   NULL
//   ["num_rows"]=>
//   int(0)
//   ["type"]=>
//   int(1)
// }
// int(-1)
// array(4) {
//   ["id"]=>
//   string(2) "67"
//   ["username"]=>
//   string(2) "3b"
//   ["password"]=>
//   string(2) "3b"
//   ["salt"]=>
//   string(2) "3b"
// }
// ========
// 第4條:
// bool(false)
// int(0)
// ========

multi_query() 就是 MySQLi 提供的執行多條 SQL 語句的方法。透過它執行之後,返回的結果是一個布林值,如果第一條語句就有問題的話,那麼它返回的就是 FALSE 。如果是後面的語句錯誤了,我們需要呼叫 next_result() 才能獲取後面語句的錯誤資訊。

其實這也引出了我們 next_result() 這個方法的作用。它就相當於是為執行獲取下一個結果的操作做準備,也可以看作是將遊標移動到了下一條 SQL 語句上。而 more_results() 方法就是判斷是否還有更多的語句沒有執行。

use_result

在上面的測試程式碼中,獲得每一條語句的執行結果使用的是 use_result() 這個方法。它的作用是啟動結果集的檢索。也就是說,在 mutli_query() 的時候,這些語句並沒有馬上執行,而是在呼叫 use_result() 時,才會執行當前的這條語句。我們注意到 INSERT 、 UPDATE 語句返回的結果都是 FALSE 。而且 SELECT 語句中的 num_rows 也是 0 。這就是它的特點,它也並沒有直接將結果集的資訊儲存在程式的記憶體中。所以,use_result() 方法最大的好處就是佔用記憶體小,適合大量查詢的遍歷,缺點則是每次都要去資料庫查詢,速度慢。

store_result

除了 use_result() 之外,還有一個 store_result() 方法可以幫助我們獲得查詢的結果集。它和 use_result() 方法是相反的,也就是說,它是直接執行就將結果集儲存在記憶體中了。

$mysqli = new mysqli("localhost", "root", "", "blog_test");

$mysqli->multi_query($sql);
$i = 1;
do{
    echo '第' . $i . '條:', PHP_EOL;
    $i++;
    $result = $mysqli->store_result();
    var_dump($result);
    var_dump($mysqli->affected_rows);
    if(is_object($result)){
        var_dump($result->fetch_assoc());
    }
    var_dump($mysqli->next_result());
    echo '========', PHP_EOL;
}
while($mysqli->more_results() );
// 第1條:
// bool(false)
// int(1)
// ========
// 第2條:
// bool(false)
// int(0)
// ========
// 第3條:
// object(mysqli_result)#1 (5) {
//   ["current_field"]=>
//   int(0)
//   ["field_count"]=>
//   int(4)
//   ["lengths"]=>
//   NULL
//   ["num_rows"]=>
//   int(7)
//   ["type"]=>
//   int(0)
// }
// int(7)
// array(4) {
//   ["id"]=>
//   string(2) "67"
//   ["username"]=>
//   string(2) "3b"
//   ["password"]=>
//   string(2) "3b"
//   ["salt"]=>
//   string(2) "3b"
// }
// ========
// 第4條:
// object(mysqli_result)#3 (5) {
//   ["current_field"]=>
//   int(0)
//   ["field_count"]=>
//   int(1)
//   ["lengths"]=>
//   NULL
//   ["num_rows"]=>
//   int(1)
//   ["type"]=>
//   int(0)
// }
// int(1)
// array(1) {
//   ["now()"]=>
//   string(19) "2020-09-14 10:31:37"
// }

不僅查詢結果中的 num_rows 有資料了,最後一條 SELECT now(); 語句也成功返回了。它和我們日常使用 query() 的結果是類似的。

另外需要注意的一點是,大家可以看一下我們執行這兩條獲取結果方式的迴圈條件是如何寫得。more_results() 和 next_result() 針對這兩種結果集的獲取方式也是不同的,大家可以自己測一下。

總結

光說不練假把式,雖說多語句執行看似很美好,但即使在這簡單的測試程式碼中,也會出現各種問題,大家一定要自己多嘗試一下。在日常的開發過程中,最好還是一條一條的語句來執行,避免出現各種無法查明的問題而影響我們正常的業務執行。至於到底要不要使用這個能力,還是大家仁者見仁智者見智了。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/810/viewspace-2797093/,如需轉載,請註明出處,否則將追究法律責任。

相關文章