이 글에서는 PHPPHP에서 설정한 쿼리 구조를 소개하겠습니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
PDO에 대한 마지막 글에서는 쿼리 결과 집합의 연산으로 마무리하겠습니다. 데이터베이스 작업에서는 쿼리가 매우 높은 비율을 차지하는 경우가 많습니다. 일상적인 개발에서 대부분의 비즈니스는 읽기/쓰기가 필요 없는 비즈니스이므로 쿼리 관련 작업을 마스터하는 것이 학습의 중요한 부분입니다. mysqli와 마찬가지로 PDO의 쿼리 지원도 매우 편리하고 빠릅니다. 몇 가지 기능을 통해 다양한 쿼리문을 매우 편리하고 효율적으로 조작할 수 있습니다.
Prepared 문을 사용할 때 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()를 사용하여 결과 집합을 탐색하고 배열에 할당하는 데 도움을 줍니다. 따라서 re-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의 형식 지정에 대해서는 PDO 개체의 fetch() 및 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视频教程
위 내용은 3분 안에 PHP에 설정된 쿼리 구조를 이해해보세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!