美文网首页
Android数据库代码优化(2) - 从SQLite说起

Android数据库代码优化(2) - 从SQLite说起

作者: Jtag特工 | 来源:发表于2017-04-26 17:17 被阅读150次

    从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 structure

    从这张官方图上,我们可以看到,除了工具和测试代码之外,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语句以及处理返回结果了。

    相关文章

      网友评论

          本文标题:Android数据库代码优化(2) - 从SQLite说起

          本文链接:https://www.haomeiwen.com/subject/yvpvzttx.html