さて、前の投稿で設定したSQLiteをぜひ使ってやろう。
注意:そのまま流用して起こった不具合は責任を負えません。脆弱な面もあるので気をつけてください。
openしてみる
まず、SQLiteのデータは、ファイルで管理されるという仕様を思い出して欲しい。つまり、ファイルを扱うのと同じ感覚でOpenして、後はクエリを送りつければ良いということ。
bool open() { if ((connect = new SQLiteConnection("Data Source=" + dbFilename)) == null) { error("DBの接続に失敗しました。"); return false; } if ((command = connect.CreateCommand()) == null) { error("SQLの実行ができません。"); return false; } connect.Open(); transaction = connect.BeginTransaction(); return true; }
このようなメソッドを作ってみた。 とりあえず大きな流れがあって、
- ファイルを開く(DBを開く)
- コマンドを作成(?)←ここの表現がいまいちわからない
- トランザクションを開始
トランザクションにおける注意点
特に、3.に注意して欲しい。どうやらSQLiteでは明示的にトランザクションを行わないと、暗示的にINSERTの前後でトランザクションが行われるようだ。
トランザクションとして管理された処理は「すべて成功」か「すべて失敗」のいずれかであることが保証される。例えば、資金移動システムをコンピュータで処理する場合、出金処理と入金処理は「どちらも成功」か「どちらも失敗」のどちらかであることが要求される。「出金に成功して入金に失敗」すると、出金された資金が宙に浮いてしまうからである。このような場合に、出金と入金をまとめて1つのトランザクションとして管理し、どちらか一方が失敗したらもう片方も失敗させ、どちらも成功したときに初めて全体を成功と評価するのがTPである。 引用
トランザクションってのはそんなに軽い処理ではないから、INSERTのたびにトランザクションを行われたら、速度は馬鹿みたいに遅くなるらしい。検証してるページはたくさんあるから、適当にググれば良いと思う。
SQLを送りつけてみる
結論としては、たくさんのINSERTを一度に行う必要がある処理では、すべてのINSERTを明示的に1つのトランザクションで囲わないと、速度的にまずいことが起こるということ。よって、SQLを実行するメソッドは以下の二つを用意すると便利そう。
public bool ExecuteCommand(string comm) { if (open()) { try { command.CommandText = comm; command.ExecuteNonQuery(); close(); return true; } catch (Exception ex) { error(ex.ToString()); } } return false; } public bool ExecuteCommand(string[] comms) { if (open()) { try { for (int i = 0; i < comms.Length; i++) { command.CommandText = comms[i]; command.ExecuteNonQuery(); } close(); return true; } catch (Exception ex) { error(ex.ToString()); } } return false; }
オーバーロードしてメソッドを宣言してある。文字列を渡せば前者が呼び出されて、文字列の配列を渡せば後者が呼び出される。という仕組みで、まとめてINSERTする場合は、前記の理由により、後者を用いたほうが、より高速。
注意点:DBを開くオーバーヘッド
- DBファイルを開く
- トランザクションを開始
- 処理を行う
- DBを閉じる
サンプルがこのようになっていることに注意。SQLを送りつけるたびにDBを開きなおしているため、何度もSQLを送りつける場合は、DBを開くためのオーバーヘッドが大きくなりすぎる。この場合は、プログラム起動時に一度だけopenし、その後のSQLごとにトランザクションのみを行うなどの工夫が必要だろう。
closeを忘れない
後、忘れてはいけないのが、資源の解放。
bool close() { transaction.Commit(); transaction.Dispose(); command.Dispose(); connect.Close(); return true; }
何気なく今までのソースでclose()を使ってきたけれども、実はこれのこと。
注意点
SQLを送りつける注意点でも書いたように、DBを閉じるオーバーヘッドも考慮するなら、この部分も変える必要がある。
ピンバック: System.Data.SQLiteのラッパ的なもの | jikkenjo.net