本篇文章要為大家介紹一下PHPPHP中的查詢結構集。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
關於PDO 的最後一篇文章,我們就以查詢結果集的操作為結束。在資料庫的操作中,查詢往往佔的比例非常高。在日常的開發中,大部分的業務都是讀多寫少型的業務,所以掌握好查詢相關的操作是我們學習的重要內容。和 mysqli 一樣,PDO 對於查詢的支援也是非常方便快速的,透過幾個函數就可以非常方便且有效率地操作各種查詢語句。
在使用預處理語句的情況下,我們使用 execute() 執行之後,查詢的結果集就會儲存在 PDOStatement 物件中。對於資料的操作就轉移到了 PHP 的物件中,所以我們需要 PDOStatement 的一些方法來獲得結果集的內容。
透過 fetch() 方法,得到的是查詢結果集的下一行。
$stmt = $pdo->prepare("select * from zyblog_test_user"); $stmt->execute(); $row = $stmt->fetch(); print_r($row); // Array // ( // [id] => 1 // [0] => 1 // [username] => aaa // [1] => aaa // [password] => aaa // [2] => aaa // [salt] => aaa // [3] => aaa // )
從傳回的結果來看,我們沒有給 PDO 物件指定 PDO::ATTR_DEFAULT_FETCH_MODE 屬性,所以它是傳回的預設的 PDO::FETCH_BOTH 格式,也就是欄位名稱和下標同時存在的。其實這個方法可以直接指定我們需要的 FETCH_STYLE 。
$row = $stmt->fetch(PDO::FETCH_ASSOC); print_r($row); // Array // ( // [id] => 2 // [username] => bbb // [password] => bbb // [salt] => 123 // ) $row = $stmt->fetch(PDO::FETCH_LAZY); print_r($row); // PDORow Object // ( // [queryString] => select * from zyblog_test_user // [id] => 3 // [username] => ccc // [password] => bbb // [salt] => c3 // ) $row = $stmt->fetch(PDO::FETCH_OBJ); print_r($row); // stdClass Object // ( // [id] => 4 // [username] => ccc // [password] => bbb // [salt] => c3 // )
和指定 PDO 物件的 PDO::ATTR_DEFAULT_FETCH_MODE 一樣。使用 fetch() 方法時直接將需要的回傳結果類型參數指定到方法的第一個參數,就實作了 FETCH_STYLE 的指定。具體支援的格式和之前講過的 PDO 物件的 PDO::ATTR_DEFAULT_FETCH_MODE 屬性是完全一樣的,大家可以自行查閱。
從程式碼和定義可以看出,fetch() 方法是取得目前資料集的下一行數據,就像資料庫的遊標操作一樣。所以,我們可以透過循環 fetch() 來對結果集進行遍歷,從而獲得所有的結果集資料。
while($row = $stmt->fetch()){ print_r($row); } // Array // ( // [id] => 2 // [0] => 2 // [username] => bbb // [1] => bbb // [password] => bbb // [2] => bbb // [salt] => 123 // [3] => 123 // ) // ……
上文中提到了遊標操作,PDO 擴充功能是支援遊標的,但是需要注意的是,MySQL 擴充功能並不支援這個操作。所以我們使用遊標相關的屬性對於 MySQL 函式庫是沒有效果的。
$stmt = $pdo->prepare("select * from zyblog_test_user", [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT); print_r($row); // Array // ( // [id] => 1 // [username] => aaa // [password] => aaa // [salt] => aaa // ) $stmt = $pdo->prepare("select * from zyblog_test_user", [PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL]); $stmt->execute(); $row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_LAST); print_r($row); // Array // ( // [id] => 1 // [username] => aaa // [password] => aaa // [salt] => aaa // )
如果是支援遊標操作的資料庫及擴充的話,上面程式碼中的 fetch() 的第二個參數指定後,所獲得的結果是會不同的。 PDO::FETCH_ORI_NEXT 是取得遊標的下一筆數據,而 PDO::FETCH_ORI_LAST 是取得遊標的最後一筆資料。但在我們對 MySQL 的測試中,它們並沒有任何效果,依然是取得結果集的下一個資料。
透過fetch() 方法,我們可以獲得結果集中的全部數據,不過還是需要一個循環才能進行遍歷,多少還是有點麻煩。其實,PDO 早就為我們準備好了另一個方法,fetchAll() 就是傳回一個包含結果集中所有行的陣列。
$stmt = $pdo->prepare("select * from zyblog_test_user limit 2"); $stmt->execute(); $list = $stmt->fetchAll(); print_r($list); // Array // ( // [0] => Array // ( // [id] => 1 // [0] => 1 // [username] => aaa // [1] => aaa // [password] => aaa // [2] => aaa // [salt] => aaa // [3] => aaa // ) // [1] => Array // ( // [id] => 2 // [0] => 2 // [username] => bbb // [1] => bbb // [password] => bbb // [2] => bbb // [salt] => 123 // [3] => 123 // ) // )
fetchAll() 就是在內部使用了 fetch() 幫我們遍歷了一次結果集並且賦值到了一個陣列中。所以我們如果在不重新 execute() 情況下再次呼叫 fetchAll() 的話,取得的就是空的資料。因為遊標已經到底了。
$list = $stmt->fetchAll(); print_r($list); // Array // ( // )
它同樣支援指定 FETCH_STYLE ,也和 fetch() 方法一樣,直接將所需的型別常數賦值給第一個參數就可以了。
// PDO::FETCH_ASSOC $stmt = $pdo->prepare("select * from zyblog_test_user limit 2"); $stmt->execute(); $list = $stmt->fetchAll(PDO::FETCH_ASSOC); print_r($list); // Array // ( // [0] => Array // ( // [id] => 1 // [username] => aaa // [password] => aaa // [salt] => aaa // ) // [1] => Array // ( // [id] => 2 // [username] => bbb // [password] => bbb // [salt] => 123 // ) // ) // PDO::FETCH_COLUMN $stmt = $pdo->prepare("select * from zyblog_test_user limit 2"); $stmt->execute(); $list = $stmt->fetchAll(PDO::FETCH_COLUMN, 1); print_r($list); // Array // ( // [0] => aaa // [1] => bbb // ) // PDO::FETCH_CLASS class User{ function __construct($a){ echo $a, PHP_EOL; } } $stmt = $pdo->prepare("select * from zyblog_test_user limit 2"); $stmt->execute(); $list = $stmt->fetchAll(PDO::FETCH_CLASS, 'User', ['FetchAll User']); print_r($list); // FetchAll User // FetchAll User // Array // ( // [0] => User Object // ( // [id] => 1 // [username] => aaa // [password] => aaa // [salt] => aaa // ) // [1] => User Object // ( // [id] => 2 // [username] => bbb // [password] => bbb // [salt] => 123 // ) // )
是不是非常熟悉了,這裡就不多講了,關於FETCH_STYLE 的類型指定已經說過很多遍了,它的用法和fetch() 以及PDO 對像中的query() 方法都是差不多的。不過它也支援一種以回調方式呼叫一個方法的形式來獲得資料集。
function getValue(){ print_r(func_get_args()); } // Array // ( // [0] => 1 // [1] => aaa // [2] => aaa // [3] => aaa // ) // Array // ( // [0] => 2 // [1] => bbb // [2] => bbb // [3] => 123 // ) $stmt = $pdo->prepare("select * from zyblog_test_user limit 2"); $stmt->execute(); $list = $stmt->fetchAll(PDO::FETCH_FUNC, 'getValue'); print_r($list); // Array // ( // [0] => // [1] => // )
在這段程式碼中,我們使用的是 PDO::FETCH_FUNC ,第二個參數是一個方法名稱。這樣每一條結構集都會在遍歷的時候當作方法的參數去呼叫指定的這個方法,我們透過 func_get_args() 就可以取得這些參數內容。在這段程式碼中,結果集並不會透過 fetchAll() 方法的回傳值賦值給 $list 變數了。因為資料都已經傳遞給了指定的 getValue() 方法了。
在上面的測試程式碼中,我們使用過 PDO::FETCH_COLUMN 來取得結果集的某一列資料。這樣寫沒什麼問題,但有更方便的方式,也就是 PDOStatment 直接提供給我們的一個 fetchColumn() 方法。它就相當於是預設的在方法內部指定了 PDO::FETCH_COLUMN ,並且只需要一個參數就是列的下標。
要注意的是,它的回傳是下一行的指定列值,也就是說,它在底層是呼叫的 fetch() 方法。如果要取得結果集中所有指定列的內容,我們還需要透過和 fetch() 的遍歷方式一樣的方法來遍歷結果集。
// fetchColumn $stmt = $pdo->prepare("select * from zyblog_test_user"); $stmt->execute(); $column = $stmt->fetchColumn(2); echo $column, PHP_EOL; // aaa $column = $stmt->fetchColumn(3); echo $column, PHP_EOL; // 123
fetchObject() 就不用多解释了,它和 fetchColumn() 是类似的,只是返回的是下一行数据的对象格式。同样的,它也是可以传递构造参数的,这点和 PDO 对象的 query() 中指定的 PDO::FETCH_CLASS 格式的使用是一样的。我们在第一篇文章中就有讲解。
// fetchObject $stmt = $pdo->prepare("select * from zyblog_test_user"); $stmt->execute(); $user = $stmt->fetchObject('User', ['FetchObject User']); print_r($user); // FetchObject User // User Object // ( // [id] => 1 // [username] => aaa // [password] => aaa // [salt] => aaa // )
要获得查询的结果集行数就需要我们的 rowCount() 方法了。数据库中不管是查询还是增、删、改操作,都会返回语句执行结果,也就是受影响的行数。这些信息都是通过 rowCount() 这个方法获得的。
需要注意的是,在查询语句中,有些数据是可能返回此语句的行数的。但这种方式不能保证对所有数据有效,且对可移植的应用更不要依赖这种方式。我们如果需要知道当前查询结果的数量,还是通过遍历 fetch() 或者通过 count(fetchAll()) 来根据真实查询到的结果集数量确定这一次查询的真实行数。
其实它就像是 PDO 对象的 exec() 方法所返回的数据。在不使用预处理语句的情况下,直接使用 PDO 的 exec() 方法执行 SQL 语句后,返回的也是语句执行后受影响的行数。
$stmt = $pdo->prepare("select * from zyblog_test_user"); $stmt->execute(); $rowCount = $stmt->rowCount(); echo $rowCount, PHP_EOL; // 41
$stmt = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(?, ?, ?)"); $stmt->execute(['kkk','666','k6']); $rowCount = $stmt->rowCount(); echo $rowCount, PHP_EOL; // 1 $id = $pdo->lastInsertId(); echo $rowCount, PHP_EOL; // 1 $stmt = $pdo->prepare("update zyblog_test_user set username=? where username = ?"); $stmt->execute(['ccc','cccc']); $rowCount = $stmt->rowCount(); echo $rowCount, PHP_EOL; // 25 $stmt = $pdo->prepare("update zyblog_test_user set username=? where username = ?"); $stmt->execute(['ccc','cccc']); $rowCount = $stmt->rowCount(); echo $rowCount, PHP_EOL; // 0 $stmt = $pdo->prepare("delete from zyblog_test_user where username = ?"); $stmt->execute(['ddd']); $rowCount = $stmt->rowCount(); echo $rowCount, PHP_EOL; // 11 $stmt = $pdo->prepare("delete from zyblog_test_user where username = ?"); $stmt->execute(['ddd']); $rowCount = $stmt->rowCount(); echo $rowCount, PHP_EOL; // 0
更新和删除操作在数据不存在、没有更新、没有删除的情况下都返回的是 0 。这一点我们也在 PDO 相关的第一篇文章中就说过了,对于业务来说,这种更新或删除到底算是成功还是失败呢?还是大家根据自己的实际业务情况来确定吧!
关于 PDO 和 PDOStatement 相关的内容就学习到这里了。我们完整地梳理了一遍它们两个所有的方法,也都进行了相关的测试。大家在日常使用中可能接触到的并不多,框架都已经为我们封装好了。不过对于学习来说,平常的小测试、小调试完全可以自己手写来加深记忆和理解。在深入理解了这些扩展类的使用方法后,反过来又能帮助我们更加的清楚框架是如何去封装它们的。总之,学习就是不断的从高层到底层,再从底层返回高层,循环往复,才能更加的得心应手。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202009/source/PHP%E4%B8%AD%E7%9A%84PDO%E6%93%8D%E4%BD%9C%E5%AD%A6%E4%B9%A0%EF%BC%88%E5%9B%9B%EF%BC%89%E6%9F%A5%E8%AF%A2%E7%BB%93%E6%9E%84%E9%9B%86.php
推荐学习:php视频教程
以上是三分鐘帶你了解PHP中的查詢結構集的詳細內容。更多資訊請關注PHP中文網其他相關文章!