開発環境で自動でSQLにExplainをかけるコンポーネント バージョンアップ(1.0)
CakePHP 1.2.1を使ってます。
昨日書いたAuto Explain Componentの記事ですが、反応はほとんどないと思って、ソースコードとか適当に書いた状態で公開してしまいました。反応があってうれしかったのですが、いくつか不具合を発見したので、いきなりバージョンアップです。
追記
すみません、この記事はMySQLのみ対応となります。PostgreSQLをご利用の方は、下記の記事を参照ください。
開発環境で自動でSQLにExplainをかけるコンポーネント バージョンアップ(1.1) PostgreSQL対応
バグとしては、モデルをnewを使って個別に呼び出してた場合や、insert, update , deleteなどが発行された場合はうまく動きませんでした。今回はそのバグの修正と、追加機能として、ある秒数以上かかったSelect文のみExplainするようにしました。デフォルトは0秒以上のクエリなので全てのSelect文がExplain対象です。
基本的にはcomponentファイルを入れ替えるだけでOKです。
コンポーネントやReadmeなどのファイルはこのZIPをダウンロードするか、下記のコードをコピー&ペーストしてください。
app/controllers/components/explain_sql.php
<?php /** * ExplainSqlComponent - Auto execute SQL Explain and set results in the debug mode. * * Copyright (c) 2009 Yasushi Ichikawa * * Use this compnent in afterFilter or afterRender. * var $components = array('ExplainSql'); * $this->ExplainSql->showExplainSQL( $slowQueryThreshold = 0 ); * * @author Yasushi Ichikawa * @version 1.0 * */ class ExplainSqlComponent extends Object{ /** * * @var controller object */ var $_controller; /** * set conroller object */ function startup(& $controller) { $this->_controller = $controller; } /** * Get all SQL query and execute SQL Explain of them without DESCRIBE query. * * if set the $slowQueryThreshold, * execute SQL Explain only slow query which are spent over $slowQueryThreshold seconds. * * @param integer $slowQueryThreshold * @access public */ function showExplainSQL( $slowQueryThreshold = 0 ){ $explain_results = array(); $count = 1; if(Configure::read() < 2 ){ return ; } if (!class_exists('ConnectionManager')) { return ; } $dbConfigs = ConnectionManager::sourceList(); foreach ( $dbConfigs as $configName ) { $db =& ConnectionManager::getDataSource( $configName ); if( empty($db->_queriesLog[0]) ){ continue; } foreach( $db->_queriesLog as $key => $value ){ if( preg_match( '/^SELECT /i', $value['query'] ) && $value['took'] >= $slowQueryThreshold ){ $reesults = null; $results = $db->query( "Explain ". $value['query'] ); $results[0][0]['query'] = $value['query']; $results[0][0]['id'] = $count; $explain_results[] = $results[0][0]; $count++; } } } if( !empty( $explain_results[0] ) ){ $this->_outputHtml( $explain_results ); } return; } /** * set SQL Explain results on the controller->output. * * @param array $explain_results */ function _outputHtml( $explain_results ){ $html_out = '<table>'; $html_out .= '<tr>'; //set table column name foreach( $explain_results[0] as $titlekey => $titleval ){ $html_out .= '<th>'; $html_out .= $titlekey; $html_out .= '</th>'; } $html_out .= '</tr>'; //set results foreach($explain_results as $recordnum => $val_arr){ $html_out .= '<tr>'; foreach( $val_arr as $key => $value ){ $html_out .= '<td style = "text-align: left">'; $html_out .= $value." "; $html_out .= '</td>'; } $html_out .= '</tr>'; } $html_out .= '</table>'; $this->_controller->output .= $html_out; } } ?>
使い方は前と同じように、afterRenderとかafterFileterに入れてください。今回の追加機能で、ある指定秒数以上かかったクエリのみExplainするために、第一引数に数値をセットするようにしましたが、これがなくても動きます(全てのSelectがExplain対象)。
今回の例は、全ての画面に表示させるために、app/app_controller.phpに記載する例です。
<?php class AppController extends Controller { var $components = array('ExplainSql'); function afterFilter(){ parent::afterFilter(); $this->ExplainSql->showExplainSQL( $slowQueryThreshold = 0 ); } } ?>
前のバージョンだと、発行されたクエリの取得を、むりやり適当なモデルを使って取得してたんですが、今回のバージョンから発行されたクエリの取得方法を変えました。さらにconfig/database.phpで$dafault以外のDBも使ってる場合でも動くようになってると思います(SelectだけスレーブDBを見ている場合とか)。
下記のmcurryさんのソースコードを参考にしました。
http://github.com/mcurry/cakephp/tree/master/plugins/sql_log