3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。(jehupc.exblog.jpから移転中)

(PHP)PDO使用時はPDOStatementをした方がよい

PDOを使って、選択クエリ発効後、挿入クエリをするスクリプトを書いています。

選択クエリは、fetch メソッドで先頭行のみ取得しており、挿入クエリでは、トランザクションを使っています。

しかし、挿入クエリの、beginTransaction() で下記のようなエラーが。。。

Fatal error: Maximum execution time of 30 seconds exceeded in D:\eclipe\testl\util.php on line 100

原因として選択クエリ後に、PDOStatement を解放してなかったのがまずいようです。

PHPリファレンス:PDO::queryには、「PDO::query() を次にコールする前に 結果セット内の全てのデータを取得しない場合、そのコールは失敗します。 PDOStatement::closeCursor() をコールし、 次に PDO::query() をコールする前に PDOStatement オブジェクトに関連付けられたリソースを解放してください。」とあります。

fetch 後、PDOStatement::closeCursor() を実行することで、ちゃんと動くようになりました。

下記のような感じです。(ADD関数は、DB追加時に汎用性を持たすようにしたものです。詳しくは、(PHP)PDOでforeachを使ってbindParamでパラメータを設定してた時の注意点を参照。)

$employeeCode = '015640';

$departmentCode = '201';

try {

// DBに接続する

$db = new PDO( 'sqlite2:./test.sqlite', '', '' );

 

//件名カテゴリ取得

$stmt = $db->prepare( 'SELECT * FROM employee WHERE employee_code = :code' );

$res = $stmt->execute(array(':code' => $employeeCode ) );

$res = $stmt->fetch(PDO::FETCH_BOTH);

$employeeId = $subject . $res['id'];

//↓これが必要。これで他の SQL ステートメントを発行できるようにサーバへの接続を解放。

$stmt->closeCursor();

//件名カテゴリ取得

$stmt = $db->prepare( 'SELECT * FROM department WHERE department_code= :code' );

$res = $stmt->execute(array(':code' => $departmentCode ) );

$res = $stmt->fetch(PDO::FETCH_BOTH);

$departmentId = $subject . $res['id'];

//↓これが必要。これで他の SQL ステートメントを発行できるようにサーバへの接続を解放。

$stmt->closeCursor();

 

//DBにデータ追加

Add('project', array('employee_id','department_id'),array($employeeId , $departmentId) );

 

// DBから切断する ( $db = null; も可能)

unset( $db );

} catch (PDOException $e) {

// DBアクセスができなかったとき

'アクセスできません : ' . $ex->getMessage();

unset( $db );

die();

}

 

/* DBにデータを追加するSQL

* $tbl:テーブル名

* $clm_ary:列名の配列

* $value_ary:値の配列($clm_aryの要素Noにあったデータを入れること)

*/

function Add($tbl,$clm_ary,$value_ary){

try {

//sql文組立て

$sql_part1 = '';

for ($i = 0; $i < count($clm_ary); $i++) {

$sql_part1 = $sql_part1 . ' ? ';

if( $i < count($clm_ary)-1) {

$sql_part1 = $sql_part1 . ",";

}

}

$sql_part2 ='';

for ($i = 0; $i < count($value_ary); $i++) {

$sql_part2 = $sql_part2 . ' ? ';

if( $i < count($value_ary)-1) {

$sql_part2 = $sql_part2 . ",";

}

}

$sql = 'INSERT INTO ?( ' . $sql_part1 . ') VALUES (' . $sql_part2 . ')';

// DBに接続する

$db = new PDO( 'sqlite2:./test.sqlite', '', '' );

//SQL警告出すようにする

$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

//トランザクション開始。 closeCursor 無いとここでエラー。

$stmt=$db->beginTransaction();

$stmt = $db->prepare( $sql );

//パラメータ指定

$i = 1;

$stmt->bindParam($i, $tbl);

foreach($clm_ary as $key => &$clm) {

$i++;

$stmt->bindParam($i, $clm);

}

unset($clm);

foreach($value_ary as $key => &$value) {

$i++;

$stmt->bindParam($i, $value);

}

unset($value);

//クエリ実行

$res = $stmt->execute();

//コミット

$stmt= $db->commit();

$db=null;

}catch( PDOException $ex ) {

// DBアクセスができなかったとき

print 'DBにデータ追加失敗。 : ' . $ex->getMessage();

unset( $db );

die();

}

}

参考:

【SQLite】PDOStatementは開放しないとだめなこともある