Using prepared SELECT statements on a MySQL database prior to MySQL 5.1.17 can lead to SERIOUS performance degradation.
Quote from http://dev.mysql.com/doc/refman/5.1/en/query-cache.html :
>> The query cache is not used for server-side prepared statements before MySQL 5.1.17 <<
The MySQL query cache buffers complete query results and is used to satisfy repeated identical queries if the underlying tables do not change in the meantime - just what happens all the time in a typical web application. It speeds up queries by a several hundred to a several thousand percent.
Obviously, it doesn't make much sense to give up query caching for the relatively small performance benefit of prepared statements (i.e. the DBMS not having to parse and optimize the same query multiple times) - so using PDO->query() for SELECT statements is probably the better choice i you're connecting to MySQL < 5.1.17.
PDO->prepare()
(PHP 5 >= 5.1.0, PECL pdo:0.1-1.0.3)
PDO->prepare() — 文を実行する準備を行い、文オブジェクトを返す
説明
PDOStatement->execute() メソッドによって実行される SQL ステートメントを準備します。 SQL ステートメントは、文が実行されるときに実際の値に置き換えられる 0 個もしくはそれ以上の名前 (:name) もしくは疑問符 (?) パラメータマークを含むことができます。 名前と疑問符パラメータを同一 SQL ステートメント中で使用することはできません。 どちらか一方か、他のパラメータ形式を使用してください。
PDOStatement->execute() をコールする際には、 文に渡すパラメータにはそれぞれ固有のパラメータマークを設定する必要があります。 ひとつのプリペアドステートメントの中で、同じ名前のパラメータマークを 複数使用することはできません。SQL 文の IN() 句などで、 ひとつのパラメータに複数の値を 割り当てることはできません。
異なるパラメータを用いて複数回実行されるような文に対し PDO->prepare() と PDOStatement->execute() をコールすることで、 ドライバがクライアントまたはサーバ側にクエリプランやメタ情報を キャッシュさせるよう調整するため、 アプリケーションのパフォーマンスを最適化します。また、 パラメータに手動でクオートする必要がなくなるので SQL インジェクション攻撃から保護する助けになります。
PDO は元々この機能をサポートしていないドライバに対して プリペアドステートメントとバインドパラメータをエミュレートします。 このため、ある形式をサポートしているがその他の形式をサポートしていない ドライバの場合、名前もしくは疑問符形式のパラメータを他の適当な値に 書き換えることも可能です。
パラメータ
- statement
-
これは対象のデータベースサーバに対して有効な SQL 文でなければなりません。
- driver_options
-
この配列は、このメソッドによって返される PDOStatement オブジェクトに対して 1 もしくはそれ以上の key=>value の組を含みます。 通常、スクロール可能なカーソルを要求するために PDO::ATTR_CURSOR に PDO::CURSOR_SCROLL を設定する場合に使用することになるでしょう。 いくつかのドライバには、準備する際に利用可能なドライバ固有の オプションがあります。
返り値
もしデータベースサーバが正常に文を準備する場合、 PDO->prepare() は PDOStatement オブジェクトを返します。 もしデータベースサーバが文を準備できなかった場合、 PDO->prepare() は FALSE を返します。
例
Example#1 名前付きパラメータを用いて SQL ステートメントを準備する
<?php
/* 値の配列を渡してプリペアドステートメントを実行する */
$sql = 'SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':calories' => 150, ':colour' => 'red'));
$red = $sth->fetchAll();
$sth->execute(array('calories' => 175, 'colour' => 'yellow'));
$yellow = $sth->fetchAll();
?>
Example#2 疑問符パラメータを用いて SQL ステートメントを準備する
<?php
/* 値の配列を渡してプリペアドステートメントを実行する */
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?');
$sth->execute(array(150, 'red'));
$red = $sth->fetchAll();
$sth->execute(array(175, 'yellow'));
$yellow = $sth->fetchAll();
?>
PDO->prepare()
15-Nov-2007 12:35
07-Apr-2007 10:41
Please note that the statement regarding driver_options is misleading:
"This array holds one or more key=>value pairs to set attribute values for the PDOStatement object that this method returns. You would most commonly use this to set the PDO::ATTR_CURSOR value to PDO::CURSOR_SCROLL to request a scrollable cursor. Some drivers have driver specific options that may be set at prepare-time"
From this you might think that scrollable cursors work for all databases, but they don't! Check out this bug report:
http://bugs.php.net/bug.php?id=34625
24-Feb-2007 03:51
A more versatile version of the below, but supporting an associative array and arbitrary number of fields:
<?php
public function matchCriteria($fields=null) {
$db=DB::conn();
$sql=array();
$paramArray=array();
if(is_array($fields)) {
foreach ($fields as $field=>$value)) {
$sql[] = $field.'=?';
$paramArray[]=$value;
}
}
$rs=$db->prepare('SELECT * FROM mytable'.(count($paramArray) ? ' WHERE '.join(' AND ',$sql) : ''));
$result=$rs->execute($paramArray);
if($result) {
return $rs;
}
return false;
}
?>
23-Feb-2007 01:03
If you need to create variable sql statements in a prepare statement...for example you may need to construct a sql query with zero, one, two, etc numbers of arguments...here is a way to do it without a lot of if/else statements needed to glue the sql together:
<?php
public function matchCriteria($field1=null,$field2=null,$field3=null) {
$db=DB::conn();
$sql=array();
$paramArray=array();
if(!empty($field1)) {
$sql[]='field1=?';
$paramArray[]=$field1;
}
if(!empty($field2)) {
$sql[]='field2=?';
$paramArray[]=$field2;
}
if(!empty($field3)) {
$sql[]='field3=?';
$paramArray[]=$field3;
}
$rs=$db->prepare('SELECT * FROM mytable'.(count($paramArray)>0 ? ' WHERE '.join(' AND ',$sql) : ''));
$result=$rs->execute($paramArray);
if($result) {
return $rs;
}
return false;
}
?>