2012年5月9日水曜日

SQL文の文字列連結はappendじゃないとダメ?

SQL文の文字列連結はappendじゃないとダメ?

StringBuilder sql = new StringBuilder("select * from ");

//参照先テーブルを条件によって変える

if(条件) {

sql.append("テーブル1");

} else {

sql.append("テーブル2");

}



っていうのは、



String sql = ”select * from ";

//参照先テーブルを条件によって変える

if(条件) {

sql+="テーブル1";

} else {

sql+="テーブル2";

}



って書いたらダメですか?

俺は、前者の書き方がどうも気にくわない。読みづらいから。

業務でviewを扱っていると、DBの構成は変えてくれないのに表示や検索方の修正案件ばかりで、

SQLで一生懸命対処ってことがしょっちゅうあって、はじめはわかりやすいSQLだったのに、

1年も経てばどんなSQLなのか、初見の人には理解不能なんて自体になりがち。

で、バカの一つ覚えのように、メモリリソースを節約するために、前者の書き方を強要されるのだけど、

SQL文なんて、動的にしたところで文字の長さや繰り返し連結のメモリ消費量ってたかが知れてるじゃないですか。

だから、後者でも良いんじゃないかと思うんだけど、ダメですか?



質問の性質上、

1.DBを表示に則して構築しなおす。

2.条件の切り分け時にSQL文全文書く

ってご提案はなしでお願いします。







append() か += か は本質的な差では無いと思う。



将来、SQL文が長大に育った場合、Stringはメモリ

を細切れにするので特に大量(酷い時は万/億のオー

ダー)の数のSQL発行をする場合なんかは効率が激落

ちする。だからStringではなくStringBuilder/String-

Bufferを使えというのは当然だと思う。



StringBuilderで後者の書き方ができないのが多少

煩わしいが、優先度は一度のソース生成≪多数の

実行時効率になるのは当然で仕方無い。それで

仕事として報酬もらってるなら尚更。



蛇足だがnew StringBuilder(予想文字列長)なら尚良い。



【追記】

個人的には質問文のコードは前者も後者も読み辛い。

ttkai00さん の ■可読性の問題 にあるコードを洗練

した感じの奴が良いかも。FROM句→WHERE句→SELECT句

の順でデータ処理されるので、この順に並べてあって

最後に一本化だと分かり易いかも。(1SQLで15テーブル

以上結合SELECTとかやらされると、穴埋め型で書いて

あるとタマランです)



製造コストが目に見えて(計量できる程度に)下がるな

ら、性能を犠牲にして保守性を高める(=製品のグレー

ドダウン)のも「顧客が望めば」良いかもしれない。

しかし、顧客が高性能(=ハイグレード)を求めて費用

を捻出しているのに、製造側が自分達の楽の為に勝手に

優先順位を性能<保守性に挿げ替えるのは、単なる背任

行為(≒[高保守性→低コスト→低性能=]安物の押し売り)。

決めるのは顧客で、製造では無い。製造側は、どちらに

も対応できるようにしておくべき。



多くの指標をレーダーチャートで表現して、総合評価す

るのは誰でも同じ。只、各指標の優先順位(重み付け)

は個別に変わるので、必ずしも面積=総合評価では無い。

コードの可読性なんて、顧客には直接関係無い。製造の

言い訳に過ぎない。そんなものを優先するのは、自分か

ら低レベル・格下ですと宣言しているようなものだ。

可読性優先なら、粗末な製品(黒電話w)渡せば済む。








可読性の問題と、パフォーマンスの問題は分けて考えた方がよいと思うのですが。



■可読性の問題



どっちもどっちだと思います。強いて言うなら、私は後者よりは前者の方がまだましという印象です。+= より append の方が「追加」という意味が理解できるので、わかりやすいと思います。



結局、SQL を2つの文字列リテラルに分けること自体が可読性の低下につながると思いますから、それをどう連結しようが、SQLとしての構造がわかりにくくなることに変わりはありません。



私なら、それが嫌なのでこうすると思います。



String tableName;

if (条件) {

tableName = "テーブル1";

} else {

tableName = "テーブル2";

}



String sql = String.format("select * from %s", tableName);



■パフォーマンスの問題



この程度のものなら、StringBuilder を使う利点はないと思います。測ってみればわかりますが、それほどの差はないでしょう。私の予想では String の方が若干速いと思います。



100回も連結すれば圧倒的に StringBuilder の方が速いですが、ぶっちゃけ、この程度ならどちらでもいいと思います。



ちなみに、前者は StringBuilder 1個、String 2個のインスタンスを作っていますし、後者は String 3個のインスタンスを作っていますから、メモリリソースの節約には、全くならないと思います。



ちなみに、上で私が書いた format を使う方法は一番遅いような気がします。測ってみたわけではありませんが予想で。







私は、String と StringBuilder の使い分けとしては、どれくらいの数連結を行うかだと思いますね。



StopWatch クラスとかを使って処理時間を計測してみてはどうですか?それで大差ないことがわかれば、説得する根拠にはなるでしょう。







蛇足ですが、私が昔同じような立場で、以下のようなロジックを見て閉口したことがあります。



StringBuilder sb = new StringBuilder();

sb.append("SELECT A,");

sb.append("B,");

sb.append("C ");

sb.append("FROM T ");





こういうケースは明確に文字列連結を使った方が速いです。



String sql = "SELECT A,"

+ "B,"

+ "C "

+ "FROM T ";



リテラル同士の連結はコンパイル時に連結されますから、このコードだとインスタンスの生成回数は1個になります。



ただし、今回のご質問のように条件文がある場合は、コンパイル時に連結されません。



※追記



すみません。思いっきり C# だと思って書いてました。Java カテゴリですね。とはいえ、原理はそう変わらないので、大きくは間違っていないと思いますが、一応ソースを Java 用に修正しておきました。



それから、StopWatch クラスは Java にはありません。



また、他の回答者さんの回答にある



> 一度のソース生成≪多数の実行時効率



というのは、私は必ずしもそうだとは考えません。



ソフトウェアの品質は、パフォーマンスだけで論じられるものではないからです。コードが見やすければその方が、保守性が高いということにもなります。ソフトウェアの品質は、機能性、信頼性、保守性、可用性など、多くの指標から総合的に判断すべきです。



たとえば、ユーザーにとって処理が10秒かかるか1秒で終わるかは大問題ですが、1.000秒で終わるか 0.999秒で終わるかは問題ではありません。1.000秒で処理できる読みにくいコードよりも、0.999秒で終わる読みやすいコードの方が、私はソフトウェアの品質としては上だと思います。



ですから、そのコードに要求されているパフォーマンスの要件等も考慮して、コードを選ぶべきと思います。



そういうことも考えずに、一律に StringBuilder を使うと決めつけてしまうプロジェクトの指針には、私も賛成できません。



※追記 uso8megaさんへ



そういう要件であれば、その通りだとは思います。



とはいえ、それは曖昧なものではなく、パフォーマンスを望むなら、RFPなりで具体的な性能要件が提示されているはずですから、開発側はそれを満たせばよいと考えます。逆にそれを満たせる範囲であれば、パフォーマンス以外の品質のためにパフォーマンスを犠牲にするケースもあり得ると考えます。



「とにかく速いのがいい」とか「できるだけ速く」と言われるお客さんもいますが、そう言われて本当にその通り作っては駄目だと思います。性能を高めることは、多くの場合、保守性や堅牢性、安全性を低くすることにつながりますから、お客さんがそう言われたとしても、どこでバランスを取るべきかは、しっかりと決めなくてはなりません。



ですからそういう場合は、具体的な指標を教えてもらって、それを満たせる範囲内で、他の品質もできるだけ確保できるようにするべきと思います。



また、保守性というのは、何も開発側が楽するためのものではありません。納品されるソフトウェアの品質の1つですから、ユーザーにとっても価値のあるものです。性能と違って目に見えにくいのは確かですが、これもとても大事だと思います。

0 件のコメント:

コメントを投稿