SELECT語句和其他的SQL查詢命令不同,它需要處理查詢結果。SQL語句的執行也需要使用mysqli_stmt對象中的execute()方法,但與mysqli對象中的query()方法不同,execute()方法的返回值並不是一個mysqli_result對象。mysqli_stmt對象提供了一種更為精巧的辦法來處理SELECT語句查詢結果:在使用execute()方法執行SQL語句完成查詢之后,使用mysqli_stmt對象中的bind_result()方法,把查詢結果的各個數據列綁定到一些PHP變量上;然后使用mysqli_stmt對象中的fetch()方法把下一條結果記錄讀取到這些變量里。如果成功地讀入下一條記錄fetch()方法返回TRUE,否則返回FALSE,或者已經讀完所有的結果記錄返回FALSE。
默認情況下,SELECT查詢結果將留在MySQL服務器上,等待fetch()方法把記錄逐條取回到PHP程序中,賦給使用bind_result()方法綁定的PHP變量上。如果需要對所有記錄而不只是一小部分進行處理,可以調用mysqli_stmt對象中的store_result()方法,把所有結果一次全部傳回到PHP程序中。這樣做不僅更有效率,而且能減輕服務器的負擔。store_result()方法是可選的,除了讀取數據不改變任何東西。以聯系人信息表contactinfo為例,使用預處理語句處理SELECT查詢結果的代碼如下所示:
<?php $mysqli = new mysqli("localhost", "mysql_user", "mysql_pwd", "demo"); // 連接MySQL數據庫 if (mysqli_connect_errno()) { // 檢查連接錯誤 printf(“連接失敗: %s<br>”, mysqli_connect_error()); exit(); } $query = "SELECT `name`, `address`, `phone` FROM `contactinfo` LIMIT 0,3"; // 聲明SELECT語句 if ($stmt = $mysqli->prepare($query)) { // 處理打算執行的SQL命令 $stmt->execute(); // 執行SQL語句 $stmt->store_result(); // 取回全部查詢結果 echo "記錄個數:".$stmt->num_rows."行<br />"; // 輸出查詢的記錄個數 $stmt->bind_result($name, $address, $phone); // 當查詢結果綁定到變量中 while ($stmt->fetch()) { // 逐條從MySQL服務取數據 printf ("%s (%s,%s)<br />", $name, $address, $phone); //格式化結果輸出 } $stmt->close(); //釋放mysqli_stmt對象占用的資源 } $mysqli->close(); //關閉與MySQL數據庫的連接 ?>
輸出結果如下所示:
- 記錄個數:3行
- 高某某 (海淀區,15801688338)
- 洛某某 (朝陽區,15801681234)
- 峰某某 (東城區,15801689876)
如果獲取SELECT語句查找到了多少條記錄,可以從mysqli_stmt對象中的num_rows屬性中檢索出來。但是,這個屬性只有在提前執行過store_result()方法,將全部查詢結果傳回到PHP程序中的情況下才可以使用。
如果在SELECT語句中也使用占位符號(?),並需要多次執行這一條語句時,也可以將mysqli_stmt對象中的bind_param()和bind_result()方法結合起來使用。代碼如下所示:
<?php $mysqli = new mysqli("localhost", "mysql_user", "mysql_pwd", "demo"); // 連接MySQL數據庫 if (mysqli_connect_errno()) { // 檢查連接錯誤 printf("連接失敗: %s<br>", mysqli_connect_error()); exit(); } // 聲明SELECT語句,按部門編號查找,使用占位符號(?)表示將要查找的部門 $query = "SELECT `name`, `address`, `phone` FROM `contactinfo` WHERE `departmentId`=? LIMIT 0,3"; if ($stmt = $mysqli->prepare($query)) { // 處理打算執行的SQL命令 $stmt->bind_param('s', $departmentId); // 綁定參數部門編號 $departmentId = "D01"; // 給綁定的變量賦上值 $stmt->execute(); // 執行SQL語句 $stmt->store_result(); // 取回全部查詢結果 $stmt->bind_result($name, $address, $phone); // 當查詢結果綁定到變量中 echo "D01部門的聯系人信息列表如下:<br />"; // 打印提示信息 while ($stmt->fetch()) { // 逐條從MySQL服務取數據 printf ("%s (%s,%s)<br />", $name, $address, $phone); // 格式化結果輸出 } echo "D02部門的聯系人信息列表如下:<br />"; // 打印提示信息 $departmentId = "D02"; // 給綁定的變量賦上新值 $stmt->execute(); // 執行SQL語句 $stmt->store_result(); // 取回全部查詢結果 while ($stmt->fetch()) { // 逐條從MySQL服務取數據 printf ("%s (%s,%s)<br />", $name, $address, $phone); //格式化結果輸出 } $stmt->close(); // 釋放mysqli_stmt對象占用的資源 } $mysqli->close(); // 關閉與MySQL數據庫的連接 ?>
輸出結果如下所示:
- D01部門的聯系人信息列表如下:
- 高某某 (海淀區,15801688338)
- 陳某某 (昌平區,15801682468)
- 白某某 (海淀區,15801689675)
- D02部門的聯系人信息列表如下:
- 洛某某 (朝陽區,15801681234)
在上面的示例中,根據提供的部門參數不同,從數據庫中分別取出兩個部門的聯系人信息。只要使用一次bind_result()方法綁定結果就可以了,並不需要每次執行都把查詢結果的各個數據列綁定到一些PHP變量上。
在生成網頁時,許多PHP腳本通常都會執行除參數以外,其他部分完全相同的查詢語句,針對這種重復執行一個查詢,每次迭代使用不同的參數情況,MySQL從4.1版本開始提供了一種名為預處理語句(prepared statement)的機制。它可以將整個命令向MySQL服務器發送一次,以后只有參數發生變化,MySQL服務器只需對命令的結構做一次分析就夠了。這不僅大大減少了需要傳輸的數據量,還提高了命令的處理效率。可以用mysqli擴展模式中提供的mysqli_stmt類的對象,去定義和執行參數化的SQL命令,mysqli_result類中包含的全部成員屬性和成員方法如表13-6和表13-7所示。
表13-6 mysqli_stmt類中的成員方法(共12個)
成員方法名 |
描 述 |
bind_param() | 該方法把預處理語句各有關參數綁定到一些PHP變量上,注意參數的先后順序 |
bind_result() | 預處理語句執行查詢之后,利用該方法將變量綁定到所獲取的字段 |
close() | 一旦預處理語句使用結果之后,它所占用的資源可以通過該方法回收 |
data_seek() | 在預處理語句中移動內部結果的指針 |
execute() | 執行准備好的預處理語句 |
fetch() | 獲取預處理語句結果的每條記錄,並將相應的字段賦給綁定結果 |
free_result() | 回收由該對象指定的語句占用的內存 |
result_metadata() | 從預處理中返回結果集原數據 |
prepare() | 無論是綁定參數還是綁定結果,都需要使用該方法准備要執行的預處理語句 |
send_long_data() | 發送數據塊 |
reset() | 重新設置預處理語句 |
store_result() | 從預處理語句中獲取結果集 |
表13-7 mysqli_stmt類中的成員屬性(共6個)
成員屬性名 |
描 述 |
$affected_rows | 返回該對象指定的最后一條語句所影響的記錄數。注意,該方法只與插入、修改和刪除三種查詢句有關 |
$errno | 返回該對象指定最近所執行語句的錯誤代碼 |
$error | 返回該對象指定最近所執行語句的錯誤描述字符串 |
$param_count | 返回給定的預處理語句中需要綁定的參數個數 |
$sqlstate | 從先前的預處理語句中返回SQL狀態錯誤代碼 |
$num_rows | 返回stmt對象指定的SELECT語句獲取的記錄數 |