最近ちょっとしたきっかけがありまして、AndroidでSQLiteを触っていたときは、基本ロジックを入れなかったし、そういうサンプルなどもなかったなと思ったのですが、実際のところ、どういうバランスが良いのだろうか、と気になったのです。
というようなことをTwitterでつぶやいてみた所、大学時代のスパハカ先輩から、「重くなるからモバイル系では基本SQLはシンプルにするよ。」とのお言葉を賜りました。
じゃぁ、どの程度までが許されるのかなー、ということで、少し確認をしてみたのが、本稿です。
実行環境
- Android 4.3 (Nexus 7 (2012))
実験
CREATE TABLE時にフィールドにCHECK制約を入れてみる。
テーブル
フィールド名 | 型 | 制約 | 備考 |
---|---|---|---|
ID | INTEGER | primary key autoincrement | ただのID |
VALUE_INT | INTEGER | - | int型のデータを受け取るだけ |
VALUE_LONG | INTEGER | - | long型のデータを受け取るだけ |
VALUE_BOOL | INTEGER | VALUE_BOOL IN (0, 1) | booleanとして1(true)とfalse(0)に制限 |
STRING_8 | TEXT | length(STRING_8) = 8 | 8 bytes 限定 |
STRING_100 | TEXT | length(STRING_100) = 100 | 100 bytes 限定 |
STRING_1000 | TEXT | length(STRING_1000) = 1000 | 1000 bytes 限定 |
STRING_LT_100 | TEXT | length(STRING_100) < 100 | 100 bytes 未満 |
コード
CHECK有りテーブル
public class SqlSampleData extends SampleData {
public static final String CREATE_TABLE = ""
+ "create table " + TABLE_NAME + " ( "
+ FIELD_ID + " integer primary key autoincrement "
+ ", " + FIELD_VALUE_INT + " integer "
+ ", " + FIELD_VALUE_LONG + " integer "
+ ", " + FIELD_VALUE_BOOL + " integer " + "check(" + FIELD_VALUE_BOOL + " IN (0, 1)) "
+ ", " + FIELD_VALUE_STRING_8 + " text " + "check(length(" + FIELD_VALUE_STRING_8 + ") = 8)"
+ ", " + FIELD_VALUE_STRING_100 + " text " + "check(length(" + FIELD_VALUE_STRING_100 + ") = 100)"
+ ", " + FIELD_VALUE_STRING_1000 + " text " + "check(length(" + FIELD_VALUE_STRING_1000 + ") = 1000)"
+ ", " + FIELD_VALUE_STRING_LT_100 + " text " + "check(length(" + FIELD_VALUE_STRING_LT_100 + ") < 100)"
+ " );";
}
CHECK無しテーブル
public class CodeSampleData extends SampleData {
public static final String CREATE_TABLE = ""
+ "create table " + TABLE_NAME + " ( "
+ FIELD_ID + " integer primary key autoincrement "
+ ", " + FIELD_VALUE_INT + " integer "
+ ", " + FIELD_VALUE_LONG + " integer "
+ ", " + FIELD_VALUE_BOOL + " integer "
+ ", " + FIELD_VALUE_STRING_8 + " text "
+ ", " + FIELD_VALUE_STRING_100 + " text "
+ ", " + FIELD_VALUE_STRING_1000 + " text "
+ ", " + FIELD_VALUE_STRING_LT_100 + " text "
+ " );";
}
親クラス
public class SampleData {
public static final String TABLE_NAME = "tab";
public static final String FIELD_ID = "id";
public static final String FIELD_VALUE_INT = "VALUE_INT";
public static final String FIELD_VALUE_LONG = "VALUE_LONG";
public static final String FIELD_VALUE_BOOL = "VALUE_BOOL";
public static final String FIELD_VALUE_STRING_8 = "STRING_8";
public static final String FIELD_VALUE_STRING_100 = "STRING_100";
public static final String FIELD_VALUE_STRING_1000 = "STRING_1000";
public static final String FIELD_VALUE_STRING_LT_100 = "STRING_LT_100";
}
確認方法
それぞれのテーブルに対し、条件に適合するデータでINSERTを実行する。
INSERT回数を100回、1000回、10000回とし、それぞれ10度繰り返して平均時間を算出。
CHECK有り : CHECK有りテーブルを利用し、Java側での検証無し。
CHECK無し : CHECK無しテーブルを利用し、Java側で同等の検証有り。
以下、確認結果はミリ秒。
確認結果
100回
回数 | CHECK有り | CHECK無し |
---|---|---|
1 | 71 | 91 |
2 | 108 | 82 |
3 | 71 | 56 |
4 | 66 | 77 |
5 | 102 | 81 |
6 | 69 | 57 |
7 | 97 | 91 |
8 | 99 | 82 |
9 | 66 | 59 |
10 | 77 | 85 |
平均 | 82.6 | 76.1 |
1000回
回数 | CHECK有り | CHECK無し |
---|---|---|
1 | 531 | 624 |
2 | 505 | 509 |
3 | 539 | 476 |
4 | 537 | 454 |
5 | 531 | 455 |
6 | 535 | 510 |
7 | 550 | 505 |
8 | 538 | 484 |
9 | 547 | 454 |
10 | 505 | 474 |
平均 | 531.8 | 494.5 |
10000回
回数 | CHECK有り | CHECK無し |
---|---|---|
1 | 4832 | 4814 |
2 | 4819 | 4473 |
3 | 4798 | 4563 |
4 | 4857 | 5030 |
5 | 4824 | 4421 |
6 | 5314 | 4303 |
7 | 5257 | 4614 |
8 | 4989 | 4446 |
9 | 4927 | 4545 |
10 | 4855 | 4306 |
平均 | 4947.2 | 4551.5 |
まとめ
すべてにおいて、CHECK無しの方がCHECK有りの92%程度の時間となりました。
スパハカ先輩のお言葉通り、SQLに検証ロジックを入れると性能が落ちるのは確かなようです。
ただ、今回の検証では8%程度の劣化なので、扱うデータ量が少なく、一回のトランザクションで何レコードも扱わないのであれば、SQLに検証を入れていても問題にならないと思います。
少しの差ですが、記述するコード量も減るのではないかと。
一方、大量のデータを一度に扱う場合は、多少といっても差が大きくなってくるので、Java側での検証実装にしなければいけないでしょう。
後は、今回はあまりまじめにやっていないのですが、検証に失敗するデータがを扱う可能性が高い場合は、Java側の検証ではじいた方が性能は良くなるはずなので、そことの兼ね合いも出てくるのかな、と思います。