SQLiteの操作とその最適化


SQLite操作を把握する
最近Rubyで七牛のdemo参加作品を書いて、sqlite 3を使って、多くの操作を使って、休暇の時間を利用して、簡単にSQLiteコマンドを素早くマスターする小さな入門をしました.
SQLiteはオープンソースのデータベースエンジンで、独立、サーバー依存なし、ゼロ構成、トランザクションサポートなどの特徴があります.SQLiteは軽量レベルを特徴とし、モバイルおよび組み込み機器で広く使用されており、世界で最も広く導入されているデータベースエンジンと公式に呼ばれています.
強力なコマンドセット
まず、sqliteがどのような強力なコマンドを提供しているかを見てみましょう.
fileos:false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
sqlite> .help .backup ?DB? FILE Backup DB (default "main") to FILE .bail ON|OFF Stop after hitting an error. Default OFF .databases List names and files of attached databases .dump ?TABLE? ... Dump the database in an SQL text format  If TABLE specified, only dump tables matching  LIKE pattern TABLE. .echo ON|OFF Turn command echo on or off .exit Exit this program .explain ?ON|OFF? Turn output mode suitable for EXPLAIN on or off.  With no args, it turns EXPLAIN on. .header(s) ON|OFF Turn display of headers on or off .help Show this message .import FILE TABLE Import data from FILE into TABLE .indices ?TABLE? Show names of all indices  If TABLE specified, only show indices for tables  matching LIKE pattern TABLE. .load FILE ?ENTRY? Load an extension library .log FILE|off Turn logging on or off. FILE can be stderr/stdout .mode MODE ?TABLE? Set output mode where MODE is one of:  csv Comma-separated values  column Left-aligned columns. (See .width)  html HTML <table> code  insert SQL insert statements for TABLE  line One value per line  list Values delimited by .separator string  tabs Tab-separated values  tcl TCL list elements .nullvalue STRING Print STRING in place of NULL values .output FILENAME Send output to FILENAME .output stdout Send output to the screen .prompt MAIN CONTINUE Replace the standard prompts .quit Exit this program .read FILENAME Execute SQL in FILENAME .restore ?DB? FILE Restore content of DB (default "main") from FILE .schema ?TABLE? Show the CREATE statements  If TABLE specified, only show tables matching  LIKE pattern TABLE. .separator STRING Change separator used by output mode and .import .show Show the current values for various settings .stats ON|OFF Turn stats on or off .tables ?TABLE? List names of tables  If TABLE specified, only list tables matching  LIKE pattern TABLE. .timeout MS Try opening locked tables for MS milliseconds .vfsname ?AUX? Print the name of the VFS stack .width NUM1 NUM2 ... Set column widths for "column" mode .timer ON|OFF Turn the CPU timer measurement on or off sqlite> 

以".「開始コマンド規則
上のすべての命令を見て、すべての命令が「開始します.通常のSQL文はフォーマットが自由で、複数行にまたがることができます.空白文字(whitespace)と注釈はどこにでも表示されます.SQLiteでは.開始コマンドには次のような制限があります.」
すべてのコマンド始めますの左に空白文字は含まれていませんすべてのコマンドは、1行の入力行にすべて含める必要があります.
すべてのコマンドはSQL文に表示できません命令は注釈を認識しない
一般的な操作
データベース・ファイルの作成
fileos:false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#          09:35:16-androidyue/tmp$ cat test.db cat: test.db: No such file or directory  #  sqlite3            09:35:28-androidyue/tmp$ sqlite3 test.db  #  sqlite,       sqlite> CREATE TABLE qn_uploaded(filePath VARCHAR(255), bucket VARCHAR(63), lastModified FLOAT); #  SQLite sqlite> .exit  #       ,     09:42:26-androidyue/tmp$ cat test.db 09:44:45-androidyue/tmp$ dedqn_uploadedCREATE TABLE qn_uploaded(filePath VARCHAR(255), bucket VARCHAR(63), lastModified FLOAT) 

既存のデータベースファイルを開く
fileos:false
1
22:56:15-androidyue~ $ sqlite3 database_file.db 

データベースの表示
fileos:false
1
2
3
4
5
sqlite> .databases seq name file --- --------------- ---------------------------------------------------------- 0 main /home/androidyue/qiniu/.qiniu.db 1 temp 

データテーブルの表示
fileos:false
1
2
sqlite> .tables qn_uploaded 

構築文の表示
fileos:false
1
2
sqlite> .schema qn_uploaded CREATE TABLE qn_uploaded(filePath VARCHAR(255), bucket VARCHAR(63), lastModified FLOAT); 

フィールド名の表示
fileos:false
1
2
3
4
5
6
7
8
9
#     sqlite> select * from qn_uploaded; /home/androidyue/Documents/octopress/public//images/email.png|droidyue|1410096518.43964  #     sqlite> .header on sqlite> select * from qn_uploaded; filePath|bucket|lastModified /home/androidyue/Documents/octopress/public//images/email.png|droidyue|1410096518.43964 

データテーブル構造とデータのエクスポート(テキスト形式)
fileos:false
1
2
3
4
5
6
sqlite> .dump qn_uploaded PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE qn_uploaded(filePath VARCHAR(255), bucket VARCHAR(63), lastModified FLOAT); INSERT INTO "qn_uploaded" VALUES('/home/androidyue/Documents/octopress/public/images/dotted-border.png','droidyue',1410096552.54864); COMMIT; 

出力の調整
sqlite 3プログラムは、8つの異なるフォーマットで結果を表示できます.これらのフォーマットは「csv」、「column」、「html」、「insert」、「line」、「list」、「tabs」、and「tcl」である.使用できます.modeコマンドによる出力フォーマットの切り替え
デフォルトの出力モードlistは、listモードを使用しています.各クエリー結果レコードは1行に出力されます.各列は1つの分割子で分割されます.デフォルトの分割子は「|」です.listモードでは、クエリー結果を追加処理(AWK処理など)したい場合に、半分の労力がかかります.
リストモード出力
fileos:false
1
2
sqlite> select * from qn_uploaded; /home/androidyue/Documents/octopress/public//images/email.png|droidyue|1410096518.43964 

リストモードスプリッタの変更
fileos:false
1
2
3
sqlite> .separator ", " sqlite> select * from qn_uploaded; /home/androidyue/Documents/octopress/public//images/email.png, droidyue, 1410096518.43964 

Lineモードの使用
1行あたりの出力フォーマットは = です.
fileos:false
1
2
3
4
5
sqlite> .mode line sqlite> select * from qn_uploaded;  filePath = /home/androidyue/Documents/octopress/public//images/email.png  bucket = droidyue lastModified = 1410096518.43964 

列モードの使用
fileos:false
1
2
3
4
sqlite> .mode column sqlite> select * from qn_uploaded; /home/androidyue/Documents/octopress/public//images/email.png droidyue 1410096518.43964 /home/androidyue/Documents/octopress/public/images/rss.png droidyue 1410096552.54764 

出力内容
出力結果
デフォルトでは、すべてのクエリー結果が標準の出力として表示されます.使用するoutputは、出力結果をファイルに指向させることができます.
fileos:false
1
2
3
4
5
6
7
8
sqlite> .output /tmp/test.txt sqlite> select * from qn_uploaded; sqlite> .exit 17:48:54-androidyue~/Documents/octopress/qiniu (master)$ cat /tmp/test.txt file bucket last ---- ------------- ---- /home/androidyue/Documents/octopress/public//images/email.png droidyue 1410096518.43964 /home/androidyue/Documents/octopress/public/images/rss.png droidyue 1410096552.54764 

バックアップとリカバリ
バックアップ
fileos:false
1
2
#   .backup ?DB? FILE Backup DB (default "main") to FILE sqlite> .backup main /tmp/main.txt 

リカバリ
fileos:false
1
2
#  .restore ?DB? FILE Restore content of DB (default "main") from FILE .restore main /tmp/main.txt

AndroidでのSQLiteパフォーマンスの最適化
データベースはアプリケーション開発でよく使われる技術で、Androidアプリケーションでも例外ではありません.AndroidではSQLiteデータベースがデフォルトで使用されていますが、アプリケーション開発では、最も多くの添削調査を使用しています.操作が簡単でも、データの検索が遅くなり、データの挿入に時間がかかる場合などがありますが、このような問題が発生した場合は、データベース操作の最適化を検討する必要があります.この文書では、開発中にデータベースをよりよく使用するための実用的なデータベース最適化操作について説明します.
インデックスの作成
多くの場合、迅速に検索するにはインデックスを作成すると聞いています.この言葉は間違いなく、データテーブルのインデックスは辞書のピンインインデックスや部首インデックスに似ています.
インデックスの作成
インデックスを作成する基本構文は次のとおりです.
1
CREATE INDEX index_name ON table_name;

単列索引の作成
1
CREATE INDEX index_name ON table_name (column_name);

索引は本当に良いですか
インデックスは、データ・テーブルの取得速度を加速させることは間違いありません.しかし、西洋のことわざ「There are two sides of a coin」のように、インデックスにも欠点があります.増加、更新、削除にとって、インデックスを使用すると遅くなります.例えば、辞書の単語を削除したい場合は、ピンインインデックスと部首インデックスの情報を削除する必要があります.インデックスを作成すると、辞書のピンインインデックスや部首インデックスなどのデータベースのサイズが増加し、実際には辞書のページ数が増加し、辞書が厚くなります.データ量の小さいテーブルにインデックスを作成すると、仕事が半分になることがよくあります.したがって、インデックスを使用するには、実際の状況を考慮して利害を考慮する必要があります.クエリーの操作レベルが大きい場合、ビジネスはクエリーの要求が高い場合、インデックスの使用を推奨します.
SQL文のコンパイル
SQLiteが操作を実行するには、プログラム内のsql文を対応するSQLiteStatementにコンパイルする必要があります.例えば、select * from recordという文は、100回実行されると100回コンパイルする必要があります.挿入または更新を一括処理する操作では、SQLiteStatementを再利用するために明示的なコンパイルを使用できます.
SQLiteStatementを再利用するのも簡単ですが、基本的には次のようになります.
sql文をコンパイルしてSQLiteStatementオブジェクトを取得し、パラメータは?を使用しての代わりに使用します.
SQLiteStatementオブジェクトをループで特定のデータバインドし、bindメソッドのindexは1から0 ではありません.
以下の簡単な使用コードを参照してください.
1
2
3
4
5
6
7
8
9
10
11
private void insertWithPreCompiledStatement(SQLiteDatabase db) {  String sql = "INSERT INTO " + TableDefine.TABLE_RECORD + "( " + TableDefine.COLUMN_INSERT_TIME + ") VALUES(?)";  SQLiteStatement statement = db.compileStatement(sql);  int count = 0;  while (count < 100) {  count++;  statement.clearBindings();  statement.bindLong(1, System.currentTimeMillis());  statement.executeInsert();  } } 

トランザクションの明示的な使用
Androidでは、SQLiteDatabaseのinsert、deleteなどの方法でもexecSQLでもトランザクションが開始され、操作のたびに原子性が確保され、結果は操作後の正しい結果であるか、操作前の結果であるかのいずれかになります.
しかし、トランザクションの実装はrollback journalファイルという名前に依存し、この一時ファイルを使用して原子操作とロールバック機能を完了します.ファイルに属する以上、Unixのファイルパターン(Open-Read/Write-Close)に合致するため、一括の修正操作に対してファイルを読み書きして閉じる操作が繰り返される.しかし、トランザクションを明示的に使用して、バッチのデータベース更新によるjournalファイルの開閉を1回に減らすことができます.
具体的な実装コードは以下の通りです.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void insertWithTransaction(SQLiteDatabase db) {  int count = 0;  ContentValues values = new ContentValues();  try {  db.beginTransaction();  while (count++ < 100) {  values.put(TableDefine.COLUMN_INSERT_TIME, System.currentTimeMillis());  db.insert(TableDefine.TABLE_RECORD, null, values);  }  db.setTransactionSuccessful();  } catch (Exception e) {  e.printStackTrace();  } finally {  db.endTransaction();  } } 

上記のコードでは、異常な投げ出しがなければ、トランザクションは成功したと考えられ、db.setTransactionSuccessful();を呼び出して操作が実際に有効であることを確認します.このプロセスで例外が発生した場合、バッチ・データは既存のテーブルに1つも挿入されません.
クエリー・データの最適化
クエリーの最適化には、インデックスの作成以外に、以下のマイクロ最適化の推奨事項があります.
必要に応じてデータ列情報を取得
通常、私たちはこのようなコードを検索するために、時間と労力を節約する目的を持っています.
1
2
3
private void badQuery(SQLiteDatabase db) {  db.query(TableDefine.TABLE_RECORD, null, null, null, null, null, null) ; } 

ここで、上記の方法の2番目のパラメータタイプはString[]であり、結果参照のcolum情報を返し、nullがcolumnデータをすべて取得する必要があることを示すことを意味する.ここでは、実際に必要とされる文字列データオブジェクトが必要とする列情報を伝えることをお勧めします.これにより、効率が向上します.
カラムインデックスの事前取得
cursorを巡る必要がある場合、私たちの一般的なやり方はこうです.
1
2
3
4
5
6
private void badQueryWithLoop(SQLiteDatabase db) {  Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;  while (cursor.moveToNext()) {  long insertTime = cursor.getLong(cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME));  } } 

しかし、ColumnIndexを取得する操作をループに言及すると、より効果的になります.修正されたコードは次のとおりです.
1
2
3
4
5
6
7
8
private void goodQueryWithLoop(SQLiteDatabase db) {  Cursor cursor = db.query(TableDefine.TABLE_RECORD, new String[]{TableDefine.COLUMN_INSERT_TIME}, null, null, null, null, null) ;  int insertTimeColumnIndex = cursor.getColumnIndex(TableDefine.COLUMN_INSERT_TIME);  while (cursor.moveToNext()) {  long insertTime = cursor.getLong(insertTimeColumnIndex);  }  cursor.close(); } 

ContentValueの容量調整
SQLiteDatabaseは便利なContentValuesを提供し、列名と値のマッピングを簡略化し、ContentValuesの内部にはHashMapを採用してKey-Valueデータを格納し、ContentValuesの初期容量は8であり、追加したデータが8を超える前に2倍の拡張操作を行うため、ContentValuesが記入した内容を推定し、合理的な初期化容量を設定することを提案する.不要な内部拡張操作を低減します.
直ちにCursorを閉じる
データベースを使用すると、Cursorを閉じるのを忘れることがよくあります.クローズされていないCursorの発見方法については、StrictModeを使用できます.詳細は、Androidパフォーマンスチューニング利器StrictModeを押してください.
時間のかかる非同期化
データベースの操作は、ローカルIOに属し、通常は時間がかかりますが、うまく処理しないとANRになりやすいので、これらの時間がかかる操作を非同期スレッドに入れて処理することをお勧めします.ここでは、単一スレッド+タスクキュー形式で処理されるHandlerThreadの非同期化を推奨します.