Android數據庫代碼優化(2) - 從SQLite說起
從SQLite說起
如果沒有SQLite的基礎,我們隻是從Android封裝的SQLite API去學習的話,難免思路會受到限製。所以,我們還是需要老老實實從頭開始學習SQLite.
當我們有一身的SQLite武功之後,再去看Android的封裝,就能更清楚如何發揮SQLite的特長。
SQLite的核心隻有一個c文件,訪問的db也存在一個文件當中。所以,我們完全可以把它嵌入到另外一個程序中。
在mac上,可以通過Homebrew來安裝。安裝之後,我們就可以用sqlite3的API來寫代碼了。
先來個能編過的sqlite3調用例子吧
我們找個網上找到的最簡單的打開關閉SQLite數據庫的例子:
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("contacts.db", &db);
if (rc)
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_close(db);
}
我們先不管它是什麼意思,先編譯一下試試:
gcc -o test_sqlite test.c -lsqlite3
然後運行一下吧,需要本地有個叫contacts.db的數據庫。
./test_sqlite
輸出為:
Opened database successfully
從上麵的例子,我們可以學習到兩個容易理解的API: sqlite3_open和sqlite3_close.
SQLite3是一個基於VDBE的數據庫引擎
有了能運行的環境之後,我們就來看看SQLite數據庫引擎的結構吧:
從這張官方圖上,我們可以看到,除了工具和測試代碼之外,SQLite的核心部分分為三部分:核心,編譯器和後端。
核心部分就是對SQL命令的處理的部分,它通過編譯器來編譯成VDBE(Virtual Database Engine)能執行的代碼。
後端是真正對數據庫進行操作的部分,包括B-樹的查找結構等。
喜歡劃重點的同學注意啦,重點來了:調用SQLite3數據庫的代碼優化的第一個點就是將編譯好的字節碼保存起來,下次用的時候直接調用。
這麼重要的功能,SQLite3 API中當然有提供,這就是後麵我們會大量學習使用的sqlite3_prepare和sqlite3_prepare_v2函數。
Android對此也有同樣的封裝,提供了SQLiteStatement來實現預編譯代碼的保存。
有同學問了,我的SQL語句並不是一成不變的,語句中的參數經常改變,這樣的話,編譯出來的代碼就沒有用了啊?
這在SQLite3的設計中當然是有考慮到的,編譯好的語句,是可以支持參數的。我們首先使用sqlite3_prepare_v2編譯,然後再通過sqlite3_bind_*函數來綁定參數。下次如果換了參數,先調用sqlite3_reset清除掉綁定信息,然後再重新用sqlite3_bind_*來做綁定新參數,就可以了。
一個調用sqlite3實現數據庫操作的功能可以用下麵的步驟來套用:
1. 根據業務需求,構造sql語句
2. 調用sqlite3_prepare_v2函數來編譯sql語句
3. 如果有參數,調用sqlite3_bind_*函數來綁定參數
4. 調用sqlite3_step函數來執行一次sql操作,直至所有操作都完成
5. 下次再使用第2步編譯出來的語句時,調用sqlite3_reset函數清理參數。然後重複第3步的操作
6. 最後,調用sqlite3_finalize來銷毀預編譯語句
下麵我們直接開始實操,首先先舉個select的例子。
我們以Android中聯係人數據庫為例,取其中的calls表的簡化版:
CREATE TABLE calls (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
sourceid TEXT,
number TEXT
...
);
雖然字段很多,我們就關注id和號碼就好。
如何寫查詢語句
我們先來一半,我們選_id和number這兩列,然後看看返回的數據中是不是兩列:
核心代碼如下:
rc = sqlite3_prepare_v2(db, sql_select_caller, -1, &stmt, &tail);
rc = sqlite3_step(stmt);
int ncols = sqlite3_column_count(stmt);
printf("The column counts of calls is:%d\n", ncols);
sqlite3_finalize(stmt);
完整版的代碼,便於大家實驗:
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
const char *sql_select_caller = "select _id, number from calls";
sqlite3_stmt *stmt;
const char *tail;
rc = sqlite3_open("contacts.db", &db);
if (rc)
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
//select _id, number from calls
rc = sqlite3_prepare_v2(db, sql_select_caller, -1, &stmt, &tail);
rc = sqlite3_step(stmt);
int ncols = sqlite3_column_count(stmt);
printf("The column counts of calls is:%d\n", ncols);
sqlite3_finalize(stmt);
sqlite3_close(db);
}
下麵我們直接調用sqlite3_step去讀每一條記錄,增加下麵一段:
while(rc == SQLITE_ROW){
printf("calls ID=%d,\t",sqlite3_column_int(stmt,0));
printf("number=%s\n",sqlite3_column_text(stmt,1));
rc = sqlite3_step(stmt);
}
如果sqlite3_step返回的結果是SQLITE_ROW,說明這一次執行取到了一條符合條件的記錄。每次取一條記錄。
完整代碼如下:
#include <stdio.h>
#include <sqlite3.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
const char *sql_select_caller = "select _id, number from calls";
sqlite3_stmt *stmt;
const char *tail;
rc = sqlite3_open("contacts.db", &db);
if (rc)
{
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else
{
fprintf(stderr, "Opened database successfully\n");
}
//select _id, number from calls
rc = sqlite3_prepare_v2(db, sql_select_caller, -1, &stmt, &tail);
rc = sqlite3_step(stmt);
int ncols = sqlite3_column_count(stmt);
printf("The column counts of calls is:%d\n", ncols);
while (rc == SQLITE_ROW)
{
printf("calls ID=%d,\t", sqlite3_column_int(stmt, 0));
printf("number=%s\n", sqlite3_column_text(stmt, 1));
rc = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
sqlite3_close(db);
}
輸出如下例:
The column counts of calls is:2
calls ID=1, number=18600009876
calls ID=2, number=18600019876
calls ID=3, number=18600029876
calls ID=4, number=18600039876
calls ID=5, number=18600049876
calls ID=6, number=18600059876
calls ID=7, number=18600069876
calls ID=8, number=18600079876
calls ID=9, number=18600089876
calls ID=10, number=18600099876
如何寫非查詢語句
上麵的例子是針對查詢語句的,我們再舉個非查詢語句的例子。比如我們試個插入的例子。
void insert_item(sqlite3 *db)
{
const char *sql_insert_sample = "insert or ignore into calls (_id,number) values (?1,?2);";
sqlite3_stmt *stmt = NULL;
const char *tail = NULL;
int rc = sqlite3_prepare_v2(db, sql_insert_sample, -1, &stmt, &tail);
sqlite3_bind_int(stmt, 1, 1000);
sqlite3_bind_text(stmt, 2, "01084993677", 11, NULL);
rc = sqlite3_step(stmt);
if (rc == SQLITE_DONE)
{
printf("Last inserted row id=%ld\n", (long)sqlite3_last_insert_rowid(db));
}
else
{
printf("Insert failed!");
}
sqlite3_finalize(stmt);
}
輸出如下:
Last inserted row id=0
小結
上麵,我們就查詢和非查詢兩種情況,學習了如何使用SQLite3的API。
剩下的工作主要就是構造SQL語句以及處理返回結果了。
最後更新:2017-04-26 18:00:56