トランザクション
データベースの処理のひとまとまりをトランザクションといいます。ひとまとまりの処理は、最初から最後まで、すべてが正しく実行されなければなりません。もし、途中でエラー等の異常事態が発生したら、それまでのすべての処理をなかったことにしなければなりません。
MySQLのInnoDBはトランザクションをサポートしています。MDB2もそのためのメソッドを実装しているので、実行して試してみましょう。
$db->beginTransaction(); $db->exec('DELETE FROM books'); $db->rollback(); //$db->commit();
beginTransaction
でトランザクションが開始されます。2行目ですべてのデータを削除していますが、3行目のrollback
によって、トランザクション開始以後の操作がすべてキャンセルされます。rollback
の代わりにcommit
と書けば、変更が確定し、元には戻せなくなります。
これは簡単な例ですが、実際にトランザクションを利用する場面では、「結果の整合性をチェックして、エラーがあればrollback
し、問題なければcommit
」という具合に実装します。
SELECT結果の処理
SELECT文の実行にはquery
メソッドを使います。
$rs = $db->query('SELECT * FROM books');
実行結果は次のように1行ずつ取り出して処理することができます。
echo '<ul>'; while($row = $rs->fetchRow()){ echo '<li>'; echo print_r($row); echo '</li>'; } echo '</ul>';
次のような結果になります。print_r
関数でこれは配列の中身を表示しています。$row[1]
とすればタイトルだけ、$row[2]
とすれば出版社だけを取り出せます。
Array ( [0] => 1 [1] => The Art of Computer Programming 1 [2] => アスキー [3] => 2004 [4] => 10290 ) 1
Array ( [0] => 2 [1] => フリーソフトウェアと自由な社会 [2] => アスキー [3] => 2003 [4] => 3360 ) 1-
Array ( [0] => 3 [1] => 計算機プログラムの構造と解釈 [2] => ピアソンエデュケーション [3] => 2000 [4] => 4830 ) 1-
Array ( [0] => 4 [1] => プログラミング作法 [2] => アスキー [3] => 2000 [4] => 2940 ) 1-
Array ( [0] => 5 [1] => Webアプリケーション構築入門 [2] => 森北出版 [3] => 2007 [4] => 3360 ) 1-
Array ( [0] => 6 [1] => プログラミングPHP [2] => O'Reilly [3] => 0 [4] => 0 ) 1-
Array ( [0] => 7 [1] => [2] => O'%Reilly [3] => 2008 [4] => 1995 ) 1-
Array ( [0] => 8 [1] => 初めてのPHP [2] => O'Reilly [3] => 0 [4] => 0 ) 1-
Array ( [0] => 9 [1] => '初めてのPHP' [2] => 'O\'Reilly' [3] => 0 [4] => 0 ) 1-
fetchRow()
はfetchRow(MDB2_FETCHMODE_ORDERED)
と同じで、結果の1行をふつうの配列で取り出します。MDB2_FETCHMODE_ASSOC
とすれば連想配列、MDB2_FETCHMODE_OBJECT
とすればオブジェクトとして取り出せます。連想配列として取り出したときは$row['title']
、オブジェクトとして取り出したときは$row->title
のようにして、個々のデータを取得できます。
ワイルドカード処理
この項は少し細かい話なので、初めて読むときはとばしてもかまいません。
この時点で、テーブルbooksの内容は次のようになっています。
id | title | publisher | year | price |
1 | The Art of Computer Programming 1 | アスキー | 2004 | 10290 |
2 | フリーソフトウェアと自由な社会 | アスキー | 2003 | 3360 |
3 | 計算機プログラムの構造と解釈 | ピアソンエデュケーション | 2000 | 4830 |
4 | プログラミング作法 | アスキー | 2000 | 2940 |
5 | Webアプリケーション構築入門 | 森北出版 | 2007 | 3360 |
6 | プログラミングPHP | O'Reilly | 0 | 0 |
7 | O'%Reilly | 2008 | 1995 | |
8 | 初めてのPHP | O'Reilly | 0 | 0 |
9 | '初めてのPHP' | 'O\'Reilly' | 0 | 0 |
このテーブルから、「O'%」から始まるpublisherの値を取り出すことを考えましょう。SELECT文でLIKE節を使うのですが、「%」があるため、少し難しくなります(「%」は任意の文字列にマッチするワイルドカード。ちなみに、「_」は任意の1文字にマッチ)。
単純なプリペアードステートメントではうまくいきません。「O'%」の「%」がワイルドカードとして処理されてしまうからです。
$stmt=$db->prepare('SELECT publisher,year FROM books WHERE publisher LIKE ?'); $rs=$stmt->execute(array($keyword.'%')); echo '<ul>'; while($row = $rs->fetchRow()) echo "<li>$row[0]</li>"; echo '</ul>';
ほしい結果(つまり「O'%Reilly」)以外のものも取得してしまっています。
O'Reilly O'%Reilly O'Reilly
次のように書くのが正解です。
$sql="SELECT publisher,year FROM books WHERE publisher LIKE 'O\'\%%'"; $rs=$db->query($sql); echo '<ul>'; while($row = $rs->fetchRow()) echo "<li>$row[0]</li>"; echo '</ul>';
正しい結果になります。
O'%Reilly
しかし、SELECT文を手で書くわけにはいきません。「'」を「\'」にする処理も自動で行わなければなりません。先に紹介したquote
メソッドは、結果を「'」で囲んでしまうので、ここでは使えません。その代わりに、escape
メソッドを使います。
$sql=("SELECT publisher,year FROM books ". "WHERE publisher LIKE '{$db->escape($keyword,true)}%'"); echo "<p>$sql</p>"; $rs=$db->query($sql); echo '<ul>'; while($row = $rs->fetchRow()) echo "<li>$row[0]</li>"; echo '</ul>';
quote
とescape
の違いは次の通りです。
quote
はSQL文で直接使えない文字を使えるように変換し、全体を「'
」で囲む(例:quote("O'Reilly")
の結果は"'O\'Reilly'")escape
はquote
と同様だが、全体を「'
」で囲まない(例:escape("O'Reilly")
の結果は"O\'Reilly")。また、2番目の引数をtrue
にすると、ワイルドカード文字(「%」と「_」)の前にバックスラッシュが付く。その結果、これらの文字がワイルドカード文字とは見なされなくなり、ここでの目的が達成される。
quote
の場合と同様で、escape
をプリペアードステートメントで使うことはできません。次のように書いても失敗します(結果無し)。
$stmt=$db->prepare('SELECT publisher,year FROM books WHERE publisher LIKE ?'); $rs=$stmt->execute(array($db->escape($keyword,true))); echo '<ul>'; while($row = $rs->fetchRow()) echo "<li>$row[0]</li>"; echo '</ul>';
どうしてもプリペアードステートメントを使いたいなら、「%」と「_」は別に処理しなければなりません。
$stmt=$db->prepare('SELECT publisher,year FROM books WHERE publisher LIKE ?'); $rs=$stmt->execute(array(strtr($keyword,array('%'=>'\%','_'=>'\_')).'%')); echo '<ul>'; while($row = $rs->fetchRow()) echo "<li>$row[0]</li>"; echo '</ul>';
「プリペアードステートメントを使うのが基本」だと先に述べましたが、ここで見たように、プリペアードステートメントだけでは完璧にはならないのです。