2014/06/07

【C#】SQLを書くときはStringBuilder.AppendLineを使うべし

プログラム内でSQL文を書くことがよくある。
しかし、記述方式がまちまちで、見難いし腹が立ってくる。

だから「こう書け!」というのをまとめてみた。


ダメなコーディング


// ダメな例
string query = String.Empty;

query += "SELECT ";
query += "    DENPYONUM ";
query += "   ,ITEM ";
query += "FROM ";
query += "    DENPYO";
query += "WHERE ";
query += "    CHUMONBI >= '20140101'";


ダメな理由


  • string型の文字結合(+)は性能問題がある
  • 抽出条件にリテラルが使用されている
  • バグが見つけにくい

なぜリテラルを使っちゃいけないかというと
こちら→【PL/SQL】性能改善のためのバインド変数(ホスト変数)の使い方 を参照してほしい。





推奨しないコーディング



// 推奨しない例
StringBuilder query = new StringBuilder();

query.Append("SELECT ");
query.Append("    DENPYONUM ");
query.Append("   ,ITEM ");
query.Append("FROM ");
query.Append("    DENPYO");
query.Append("WHERE ");
query.Append("    CHUMONBI >= @DATE ");


推奨しない理由


  • ログやデバッグ時のSQLが1列になるため、読みにくい
  • バグが見つけにくい

StringBuilderを使っているため、性能的には問題ない。
しかし、バグが見つけにくい。

実際、上記コードにはDBExceptionになるバグが隠されている。
どこかわかるだろうか?

そのバグは、7行目のquery.Append("    DENPYO");の部分。
もうお気づきの方はいつかもしれないが、このSQL文は~ DENPYOWHERE CHUMONBI ~となる。




推奨するコーディング



// 推奨する例
StringBuilder query = new StringBuilder();

query.AppendLine("SELECT");
query.AppendLine("    DENPYONUM");
query.AppendLine("   ,ITEM");
query.AppendLine("FROM");
query.AppendLine("    DENPYO");
query.AppendLine("WHERE");
query.AppendLine("    CHUMONBI >= @DATE");


推奨する理由


  • StringBuilderを使用しているため、性能問題はない
  • 文字列の前後にスペースを入れる必要がない
  • ログに改行された状態で表示されるため、読みやすい




例外だってある(性能重視!)



推奨コーディングを紹介したが、シビアな性能が求められている場合は、一概にこうとは言えない。



StringBuilder.Appendを使う


AppendとAppendLineの違いは、後ろに改行コードがつくかどうか。
コンマ何秒の性能改善が求められる場合は、Appendを使うことでDBMSのキャッシュを圧迫しなくてすむ。



余分なスペースを消す


Appendを使う理由同様に、余分なスペースを消すことでDBMSのキャッシュを圧迫しなくてすむ。
特に長いSQLや、無駄なコメントが書かれている場合に有効だ。



StringBuilderのキャパシティを指定する


StringBuilder.Capacityで想定される最大文字列を指定しておくと、必要以上にメモリを割り当てなくてすむ。
また、StringBuilderの仕様として、文字列が指定容量を超えた時に、自動的に大きなバッファを割り当て、今までのデータをコピーする。
だからCapacityの値を頻繁に更新させないために、最初に必要容量を確保しておく。


参考:StringBuilder.Capacity プロパティ (System.Text)


// 性能を求めて
StringBuilder query = new StringBuilder(128);

query.Append("SELECT ");
query.Append("DENPYONUM ");
query.Append(",ITEM ");
query.Append("FROM ");
query.Append("DENPYO ");
query.Append("WHERE ");
query.Append("CHUMONBI >= @DATE");

ポイントは2点。
1点目は、あらかじめCapacityを決めておく。
規定は16で、16を超える度にバッファが倍々に増えていき性能が落ちるため。

2点目は、1行でstring query = “SELECT DENPYONUM ~”;と書いてもいいけど、最低限の可読性・保守性を残しておくこと。




以上

0 件のコメント :

コメントを投稿