预处理语句¶
定义¶
在生成网页时,许多PHP脚本通常都会执行除参数之外,其他部分完全相同的查询语句。针对这种重复执行一个查询,每次迭代使用不同参数的情况, PDO提供了一种名为预处理语句(prepared statement)的机制。它可以将整个SQL命令向数据库服务器发送一次,以后只有参数发生变化, 数据库服务器只需对命令的结构做一次分析就够了,即编译一次,可以多次执行。会在服务器上缓存查询的语句和执行过程, 而只在服务器和客户端之间传输有变化的列值,以此来消除这些额外的开销。这样不仅大大减少了需要传输的数据量,还提高了命令的处理效率。 可以有效防止SQL注入,在执行单个查询时快于直接使用query()或exec()的方法,速度快且安全。
准备语句¶
重复执行一个SQL查询, 通过每次迭代使用不同的参数, 这种情况下使用预处理语句运行效率最高。使用预处理语句, 首先需要在数据库服务器中先准备好一个SQL语句, 但并不需要马上执行。PDO支持使用“占位符”语法, 将变量绑定到预处理的SQL语句中。 准备好一个带有占位符的SQL语句, 在数据库服务器的缓存区等待处理, 然后再去单独赋给占位符具体的值, 最后通过这个准备好的预处理语句执行。
在PDO中有两种占位符:“名字参数”和“问号参数”, 具体使用哪一种看个人的喜好。 使用名字占位符需要自定义一个字符串作为名字参数, 每个名字参数需要冒号(:)开始, 参数的命名一定要有意义, 最好和对应的字段名称相同。
使用名字参数占位符的INSERT语句如下所示:
$dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
使用问号参数作为占位符的INSERT语句如下所示:
$dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
注:问号参数一定要和字段的位置顺序对应, 不管是使用哪一种参数作为占位符构成的查询, 或是语句中没有用到占位符, 都需要使用PDO对象中的prepare()方法, 去准备这个将要用于迭代执行的查询, 并返回PDOStatement类对象。
绑定参数¶
当SQL语句通过PDO对象中的prepare()方法, 在数据库服务器端准备好之后, 如果使用了占位符, 就需要在每次执行时替换输入的参数。 可以通过PDOStatement对象中的bindParam()方法, 把参数变量绑定到准备好的占位符上(位置或名字要对应)。 此变量作为引用被绑定, 并只在PDOStatement::execute()被调用的时候才取其值。
使用bindParam()方法绑定名字参数的示例如下:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
使用bindParam()方法绑定问号参数的示例如下:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
编程示例¶
用名字参数占位符进行预处理语句重复插入的编程示例如下:
<?php
$dsn = "aci:dbname=localhost:2003/osrdb;charset=ZHS16GBK";
$user = "sysdba";
$pass = "szoscar55";
$dbh = new PDO($dsn, $user, $pass);
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);
// 插入一行
$name = 'one';
$value = 1;
$stmt->execute();
// 用不同的值插入另一行
$name = 'two';
$value = 2;
$stmt->execute();
?>
用问号参数占位符进行预处理语句重复插入的编程示例如下:
<?php
$dsn = "aci:dbname=localhost:2003/osrdb;charset=ZHS16GBK";
$user = "sysdba";
$pass = "szoscar55";
$dbh = new PDO($dsn, $user, $pass);
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$stmt->bindParam(1, $name);
$stmt->bindParam(2, $value);
// 插入一行
$name = 'one';
$value = 1;
$stmt->execute();
// 用不同的值插入另一行
$name = 'two';
$value = 2;
$stmt->execute();
?>
使用预处理语句获取数据的编程示例如下:
<?php
$dsn = "aci:dbname=localhost:2003/osrdb;charset=ZHS16GBK";
$user = "sysdba";
$pass = "szoscar55";
$dbh = new PDO($dsn, $user, $pass);
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
$stmt->execute([$_GET['name']]);
while ($row = $stmt->fetch()) {
print_r($row);
}
?>
占位符的无效使用编程示例如下
<?php
$dsn = "aci:dbname=localhost:2003/osrdb;charset=ZHS16GBK";
$user = "sysdba";
$pass = "szoscar55";
$dbh = new PDO($dsn, $user, $pass);
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$stmt->execute([$_GET['name']]);
// 占位符必须被用在整个值的位置
$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$stmt->execute(["%$_GET[name]%"]);
?>