複数行からなるSQLファイルがあったとして、それを一括で実行したいとします。
insert into foo (...) values (...)
update foo set ... where ...
insert into bar (...) values (...)
update bar set ... where ...
これらを mysql
コマンドラインから一気に流すわけですが、一気で流すなら明示的にトランザクション内で実行したほうが早いのは誰でも予想できると思います。
実際、ある40000行のupdate文を明示的にトランザクション内で実行した場合と、実行しない場合の実行速度をtime
コマンドで計測してみました。
- 明示的トランザクション内
$ time mysql < sqls_with_transaction.sql
real 0m21.692s
user 0m0.756s
sys 0m1.524s
- 明示的トランザクションなし
$ time mysql < sqls_no_transaction.sql
real 1m53.670s
user 0m0.916s
sys 0m1.432s
6倍近くの差が出てしまいました。差は歴然ですね。
さて、ここからが本題。
明示的トランザクション下で実行するのであれば、begin;
〜 commit;
で SQL文を囲めばよいわけですが、元のSQLファイルを修正できない(面倒な)場合はどうしたら良いでしょうか。
PostgreSQLのコマンドラインであるpsql
であれば、-1 (--single-transaction)
というオプションがあり、自動的に明示的トランザクション下でSQL文を実行してくれます。しかし、mysql
にはありません。
そこでsed
を使います。sed
にはi
コマンド(指定行の前に行を追加)、a
コマンド(指定行の後に行を追加)があるので、
cat sqls_no_transaction.sql | sed -e '1ibegin;' -e '$acommit;'
とすれば、先頭行の前にbegin;
、最終行の後ろにcommit;
が追加できます。
begin;
insert into foo (...) values (...)
update foo set ... where ...
insert into bar (...) values (...)
update bar set ... where ...
commit;
あとはこれをパイプでmysql
に流すだけです。
cat sqls_no_transaction.sql | sed -e '1ibegin;' -e '$acommit;' | mysql
sed
というとs
コマンド(正規表現による置換)がポピュラーですが、ファイルの内容をちょっと一時的に加工したい場合に便利なツールなのだなと改めて実感しました。
使いこなすのは相当難しいとは思いますが。(笑)
追記: mac OS X上のsedはbsd系の実装なので、sedのコマンド指定方法が異なります。以下を使ってください。
cat sqls_no_transaction.sql | sed -e '1i\'$'\n''begin;' -e '$a\'$'\n''commit;' | mysql