SqlCommand や DataTable を使うときに文字列をたくさん使うのですが、果たして世間一般(?)で言われているほど、String は遅く、StringBuilder は早いのでしょうか?というベンチマークです。
非常に長い場合は StringBuilder を使うほうが良いのですが、SqlCommand などに渡す SQL 文程度(長くても 5000 文字ぐらい)は、どうなのでしょうか?ということです。
■結論
結論から言えば、大して変わりません。以下は 20,000 件のデータを廻したときの結果です。
avg. Normal 7.54 sec
avg. StirngBuilder 7.22 sec
avg. SqlCommand 7.28 sec
違いは 2,3 % ぐらいしかないので誤差の範囲ですね。
以下は、ベンチマーク用の実験コード
■通常の String パターン
''' <summary> ''' 1.通常の string の連結で作成 ''' </summary> ''' <remarks></remarks> Public Sub TestNormal() Dim s As String = toMD5(DateTime.Now.ToString()) Dim cn As New SqlConnection("") For j As Integer = 0 To MAX - 1 Dim sql As String = "" ' よくある string.Format を使った作り方 sql += "SELECT * FROM DUAL " sql += " WHERE 1 = 1 " sql += String.Format(" AND col0 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col1 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col2 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col3 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col4 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col5 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col6 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col7 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col8 = '{0}' ", s) : s = toMD5(s) sql += String.Format(" AND col9 = '{0}' ", s) : s = toMD5(s) Dim dt As New DataTable Dim da As New SqlDataAdapter(sql, cn) 'dummy Next End Sub
string.Format を使って、ぽちぽちと文字列をフォーマットしていきます。
■StringBuilder を使ったパターン
よくあるように、文字列の連結は StringBuilder を使えッ!!! ってことなので、使ってみたのですが、大して変わりません。もっと長い SQL の場合、効果があるんでしょうが…そんなに長い SQL を書くのはどうなの???ってことなのです。
''' <summary> ''' 2.StringBuilder を使う ''' </summary> ''' <remarks></remarks> Public Sub TestStringBuilder() Dim s As String = toMD5(DateTime.Now.ToString()) Dim cn As New SqlConnection("") For j As Integer = 0 To MAX - 1 Dim sql As String = "" ' よくある StringBuilder を使った作り方 Dim sb As New System.Text.StringBuilder("") sb.Append("SELECT * FROM DUAL ") sb.Append(" WHERE 1 = 1 ") sb.AppendFormat(" AND col0 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col1 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col2 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col3 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col4 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col5 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col6 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col7 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col8 = '{0}' ", s) : s = toMD5(s) sb.AppendFormat(" AND col9 = '{0}' ", s) : s = toMD5(s) Dim dt As New DataTable Dim da As New SqlDataAdapter(sql, cn) 'dummy Next End Sub
■SqlCommand を使う
SqlCommand を使うと、最初に SQL 文を使うので文字列編集部分が極端に減ります。
これの場合は、20000 回の編集が 1 回になるわけですが、劇的に早くなる…はずなんですけど、結果は変わりません。
''' <summary> ''' SqlCommand で Const/Dim を使う ''' </summary> ''' <remarks></remarks> Public Sub TestConst() Dim s As String = toMD5(DateTime.Now.ToString()) Dim cn As New SqlConnection("") ' ここは dim でも同じ Const sql As String = _ "SELECT * FROM DUAL0 " + _ " WHERE 1 = 1 " + _ " AND col0 = @col0 " + _ " AND col1 = @col1 " + _ " AND col2 = @col2 " + _ " AND col3 = @col3 " + _ " AND col4 = @col4 " + _ " AND col5 = @col5 " + _ " AND col6 = @col6 " + _ " AND col7 = @col7 " + _ " AND col8 = @col8 " + _ " AND col9 = @col9 ; " Dim cmd As New SqlCommand(sql, cn) cmd.Parameters.Add(New SqlParameter("@col0", Nothing)) cmd.Parameters.Add(New SqlParameter("@col1", Nothing)) cmd.Parameters.Add(New SqlParameter("@col2", Nothing)) cmd.Parameters.Add(New SqlParameter("@col3", Nothing)) cmd.Parameters.Add(New SqlParameter("@col4", Nothing)) cmd.Parameters.Add(New SqlParameter("@col5", Nothing)) cmd.Parameters.Add(New SqlParameter("@col6", Nothing)) cmd.Parameters.Add(New SqlParameter("@col7", Nothing)) cmd.Parameters.Add(New SqlParameter("@col8", Nothing)) cmd.Parameters.Add(New SqlParameter("@col9", Nothing)) For j As Integer = 0 To max - 1 cmd.Parameters("@col0").Value = s : s = toMD5(s) cmd.Parameters("@col1").Value = s : s = toMD5(s) cmd.Parameters("@col2").Value = s : s = toMD5(s) cmd.Parameters("@col3").Value = s : s = toMD5(s) cmd.Parameters("@col4").Value = s : s = toMD5(s) cmd.Parameters("@col5").Value = s : s = toMD5(s) cmd.Parameters("@col6").Value = s : s = toMD5(s) cmd.Parameters("@col7").Value = s : s = toMD5(s) cmd.Parameters("@col8").Value = s : s = toMD5(s) cmd.Parameters("@col9").Value = s : s = toMD5(s) Dim dt As New DataTable Dim da As New SqlDataAdapter(cmd) 'dummy Next End Sub
以上、こんな風に実験してみると普通に string を使っている限りはスピードに変化はありません、ってことです。
ただし、データベースアクセスに関しては、単純な SqlDataAdapter の呼び出しよりも、SqlCommand でプリコンパイル SQL を使ったほうが、10 倍以上早くなるので、件数が多い場合はパフォーマンスに注意が必要です。
根本的にいろいろ間違ってますね。
まず、文字列連結有無にかかわらずバインド変数を使うべきです。
それと、文字列連結のスピードは連結回数の総和が問題では無く、大きな文字列に連結することが問題となります。
例えば、csv出力なんかで、1行のデータを作成する為に連結する場合はstring結合はまったく問題になりませんが、行そのものも縦方向に連結する際には問題となります。
I see. このネタは、文字列連結してSQL組み立てるとアカンからストアド使いましょう、のネタのつもりだったのですが、実際組み立ててみたら(ストアドを使ってないけど)「たいして時間が変わらなかった」ってネタ話です。その頃、「Java で StringBuilder を使わねばいかん」って話が盛り上がっていたので、別に += でも変わらん、っていうカウンターでもありましたが。
ストアドを使用したほうがいいのはそもそもVBA的な観点からではないでしょう・・
この例ですと文字列の連結よりFormatの解釈処理に時間がかかって
差が付かなかったのではないでしょうか?
.NET の GC を見ると、半分ぐらいは文字列処理に使っているので、その可能性は高いですね。const 文字列を使うよりも、enum で int 型を使ったほうが早い(メモリの解放とか文字列処理がない分だけ早い)ってのを、一度やってみたいと思ってます。