hibernateなどのORMを使ったりするとSQLを自動生成して発行してくれるので、最近ではほとんどSQLを書かないでもDBを使ったプログラムが書けるようになりました。ちょっとした結合はJPQLで済ませてしまうことも多いです。
とはいえ性能に問題があるときには生成されたSQLに向き合わないといけません。hibernateであればシステム変数hibernate.use_sql_comments
にtrue
を設定すると生成されたSQLがログに出力されるので、これを直接DBへ投げて原因を探ることができるわけです。
吐き出されるSQLは基本的に PreparedStaetment
を用いたものなので引数はすべてクエスチョンマーク ? になっています。JPQLでバインド変数を :value1
とかにしていても一律 ? に置き換わります。例えばこんな感じ。
/* from models.UserMst where loginId = :user and deletedAt is null */
select usermst0_.id as id78_, usermst0_.loginId as loginId78_, usermst0_.password as password78_, …
from user_mst usermst0_ where usermst0_.loginId=? and (usermst0_.deletedAt is null) limit ?
これをそのままmysqlに投げると ? のところでエラーになってしまいます。そこで下記のように記述します。(?の部分を実際に引数で置換してもよいですが、定数となると実行計画が変わってしまうことがあります。)
PREPARE my_stmt FROM '
select usermst0_.id as id78_, usermst0_.loginId as loginId78_, usermst0_.password as password78_, …
from user_mst usermst0_ where usermst0_.loginId=? and (usermst0_.deletedAt is null) limit ?'
;
SET @LoginId = 'c12345';
SET @LimitCnt = 1;
EXECUTE my_stmt USING @LoginId, @LimitCnt;
このように引数を変数として定義し、クエリ実行時に渡すことで実際のプログラム実行時と同じ条件で実行することができます。
実行計画を見たいときは select句の前に explain と記述すればよいです。テーブルを参照するのにインデックスが利用されているか、どの順番に結合されているか、などが分かります。
実行計画の詳しい読み方に付いてはここでは割愛、詳しく解説されているサイトもあるようなので。