Android數(shù)據(jù)庫(kù)代碼優(yōu)化(2) - 從SQLite說(shuō)起

從SQLite說(shuō)起

如果沒(méi)有SQLite的基礎(chǔ),我們只是從Android封裝的SQLite API去學(xué)習(xí)的話微驶,難免思路會(huì)受到限制腮鞍。所以,我們還是需要老老實(shí)實(shí)從頭開(kāi)始學(xué)習(xí)SQLite.
當(dāng)我們有一身的SQLite武功之后道盏,再去看Android的封裝而柑,就能更清楚如何發(fā)揮SQLite的特長(zhǎng)。

SQLite的核心只有一個(gè)c文件荷逞,訪問(wèn)的db也存在一個(gè)文件當(dāng)中媒咳。所以,我們完全可以把它嵌入到另外一個(gè)程序中种远。

在mac上涩澡,可以通過(guò)Homebrew來(lái)安裝。安裝之后坠敷,我們就可以用sqlite3的API來(lái)寫(xiě)代碼了妙同。

先來(lái)個(gè)能編過(guò)的sqlite3調(diào)用例子吧

我們找個(gè)網(wǎng)上找到的最簡(jiǎn)單的打開(kāi)關(guān)閉SQLite數(shù)據(jù)庫(kù)的例子:

#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

然后運(yùn)行一下吧膝迎,需要本地有個(gè)叫contacts.db的數(shù)據(jù)庫(kù)粥帚。

./test_sqlite

輸出為:

Opened database successfully

從上面的例子,我們可以學(xué)習(xí)到兩個(gè)容易理解的API: sqlite3_open和sqlite3_close.

SQLite3是一個(gè)基于VDBE的數(shù)據(jù)庫(kù)引擎

有了能運(yùn)行的環(huán)境之后限次,我們就來(lái)看看SQLite數(shù)據(jù)庫(kù)引擎的結(jié)構(gòu)吧:

sqlite structure

從這張官方圖上芒涡,我們可以看到,除了工具和測(cè)試代碼之外,SQLite的核心部分分為三部分:核心拖陆,編譯器和后端弛槐。

核心部分就是對(duì)SQL命令的處理的部分,它通過(guò)編譯器來(lái)編譯成VDBE(Virtual Database Engine)能執(zhí)行的代碼依啰。
后端是真正對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作的部分乎串,包括B-樹(shù)的查找結(jié)構(gòu)等。

喜歡劃重點(diǎn)的同學(xué)注意啦速警,重點(diǎn)來(lái)了:調(diào)用SQLite3數(shù)據(jù)庫(kù)的代碼優(yōu)化的第一個(gè)點(diǎn)就是將編譯好的字節(jié)碼保存起來(lái)叹誉,下次用的時(shí)候直接調(diào)用。
這么重要的功能闷旧,SQLite3 API中當(dāng)然有提供长豁,這就是后面我們會(huì)大量學(xué)習(xí)使用的sqlite3_prepare和sqlite3_prepare_v2函數(shù)。
Android對(duì)此也有同樣的封裝忙灼,提供了SQLiteStatement來(lái)實(shí)現(xiàn)預(yù)編譯代碼的保存匠襟。

有同學(xué)問(wèn)了,我的SQL語(yǔ)句并不是一成不變的该园,語(yǔ)句中的參數(shù)經(jīng)常改變酸舍,這樣的話,編譯出來(lái)的代碼就沒(méi)有用了袄锍酢啃勉?
這在SQLite3的設(shè)計(jì)中當(dāng)然是有考慮到的,編譯好的語(yǔ)句双妨,是可以支持參數(shù)的淮阐。我們首先使用sqlite3_prepare_v2編譯,然后再通過(guò)sqlite3_bind_函數(shù)來(lái)綁定參數(shù)刁品。下次如果換了參數(shù)泣特,先調(diào)用sqlite3_reset清除掉綁定信息,然后再重新用sqlite3_bind_來(lái)做綁定新參數(shù)哑诊,就可以了群扶。

一個(gè)調(diào)用sqlite3實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作的功能可以用下面的步驟來(lái)套用:

  1. 根據(jù)業(yè)務(wù)需求及刻,構(gòu)造sql語(yǔ)句
  2. 調(diào)用sqlite3_prepare_v2函數(shù)來(lái)編譯sql語(yǔ)句
  3. 如果有參數(shù)镀裤,調(diào)用sqlite3_bind_*函數(shù)來(lái)綁定參數(shù)
  4. 調(diào)用sqlite3_step函數(shù)來(lái)執(zhí)行一次sql操作,直至所有操作都完成
  5. 下次再使用第2步編譯出來(lái)的語(yǔ)句時(shí)缴饭,調(diào)用sqlite3_reset函數(shù)清理參數(shù)暑劝。然后重復(fù)第3步的操作
  6. 最后,調(diào)用sqlite3_finalize來(lái)銷(xiāo)毀預(yù)編譯語(yǔ)句

下面我們直接開(kāi)始實(shí)操颗搂,首先先舉個(gè)select的例子担猛。
我們以Android中聯(lián)系人數(shù)據(jù)庫(kù)為例,取其中的calls表的簡(jiǎn)化版:

CREATE TABLE calls (
_id INTEGER PRIMARY KEY AUTOINCREMENT,
sourceid TEXT,
number TEXT
...
);

雖然字段很多,我們就關(guān)注id和號(hào)碼就好傅联。

如何寫(xiě)查詢語(yǔ)句

我們先來(lái)一半先改,我們選_id和number這兩列,然后看看返回的數(shù)據(jù)中是不是兩列:
核心代碼如下:

    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);

完整版的代碼蒸走,便于大家實(shí)驗(yàn):

#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);
}

下面我們直接調(diào)用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返回的結(jié)果是SQLITE_ROW,說(shuō)明這一次執(zhí)行取到了一條符合條件的記錄比驻。每次取一條記錄该溯。

完整代碼如下:

#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

如何寫(xiě)非查詢語(yǔ)句

上面的例子是針對(duì)查詢語(yǔ)句的,我們?cè)倥e個(gè)非查詢語(yǔ)句的例子别惦。比如我們?cè)噦€(gè)插入的例子狈茉。

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

小結(jié)

上面,我們就查詢和非查詢兩種情況掸掸,學(xué)習(xí)了如何使用SQLite3的API氯庆。
剩下的工作主要就是構(gòu)造SQL語(yǔ)句以及處理返回結(jié)果了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扰付,一起剝皮案震驚了整個(gè)濱河市点晴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悯周,老刑警劉巖粒督,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異禽翼,居然都是意外死亡屠橄,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)闰挡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锐墙,“玉大人,你說(shuō)我怎么就攤上這事长酗∠保” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵夺脾,是天一觀的道長(zhǎng)之拨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)咧叭,這世上最難降的妖魔是什么蚀乔? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮菲茬,結(jié)果婚禮上吉挣,老公的妹妹穿的比我還像新娘派撕。我一直安慰自己,他們只是感情好睬魂,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布终吼。 她就那樣靜靜地躺著,像睡著了一般氯哮。 火紅的嫁衣襯著肌膚如雪衔峰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,696評(píng)論 1 312
  • 那天蛙粘,我揣著相機(jī)與錄音垫卤,去河邊找鬼。 笑死出牧,一個(gè)胖子當(dāng)著我的面吹牛穴肘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舔痕,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼评抚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伯复?” 一聲冷哼從身側(cè)響起慨代,我...
    開(kāi)封第一講書(shū)人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啸如,沒(méi)想到半個(gè)月后侍匙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡叮雳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年想暗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帘不。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡说莫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寞焙,到底是詐尸還是另有隱情储狭,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布捣郊,位于F島的核電站辽狈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏模她。R本人自食惡果不足惜稻艰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侈净。 院中可真熱鬧尊勿,春花似錦、人聲如沸畜侦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旋膳。三九已至澎语,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間验懊,已是汗流浹背擅羞。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留义图,地道東北人减俏。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碱工,于是被迫代替她去往敵國(guó)和親娃承。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容