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语句以及处理返回结果了。

时间: 2024-10-22 20:00:53

Android数据库代码优化(2) - 从SQLite说起的相关文章

转载-Android数据库高手秘籍(一)——SQLite命令

原文地址: http://blog.csdn.net/guolin_blog/article/details/38461239 Android数据库高手秘籍(一)——SQLite命令 分类: Android数据库高手秘籍2014-09-04 09:10 7310人阅读 评论(42) 收藏 举报 Android数据库SQLite高手秘籍LitePal 转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/38461239 要想熟练地操作任

Android数据库高手秘籍(一)——SQLite命令

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/38461239 要想熟练地操作任何一个数据库,最最基本的要求就是要懂SQL语言,这也是每个程序员都应该掌握的技能.虽说SQL博大精深,要想精通确实很难,但最基本的一些建表命令,增删改查,大家还是必须要学会的. SQL(Structured Query Language)是一种标准的数据库查询语言,即所有的关系型数据库都会支持它,只不过每种数据库对SQL语言的支持与标准存在着细微的不

Android数据库高手秘籍:SQLite命令

要想熟练地操作任何一个数据库,最最基本的要求就是要懂SQL语言,这也是每个程序员都应该掌握的技能.虽说SQL博大精深,要想精通确实很难,但最基本的一些建表命令,增删改查,大家还是必须要学会的. SQL(Structured Query Language)是一种标准的数据库查询语言,即所有的关系型数据库都会支持它,只不过每种数据库对SQL语言的支持与标准存在着细微的不同.我们无须关心其它数据库对SQL语言的支持情况,这里我们只要把重点放在SQLite上就可以了.下面我将使用模拟器来对SQLite支

Android数据库高手秘籍(二)——创建表和LitePal的基本用法

文章中我们学习了一些Android数据库相关的基础知识,和几个颇为有用的SQLite命令,都是直接在命令行操作的.但是我们都知道,数据库是 要和程序结合在一起使用的,单独对一个数据库去进行増删改查操作并没有什么意义,因此今天我们就来学习一下如何在Android程序当中去操作 SQLite数据库,还没看过前一篇文章的朋友可以先去参考 Android数据库高手秘籍(一)——SQLite命令 . 操作数据库的第一步当然是创建表了,传统创建表的方法相信大多数人都知道,那么今天我除了会展示传统的建表方法之

Android数据库高手秘籍(零)——前言

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/38083103 在我刚开始接触Android的时候甚至都不敢相信,Android系统竟然是内置了数据库的!好吧,是我太孤陋寡闻了.由于我之前是从Web方向转过来的,对数据库方面的技术也是特别有偏好,当知道这一消息之后确实颇为惊喜.Android中内置的数据库是SQLite,而我之前做Web开发时最常用的数据库是MySQL,经过对比之后我发现这两个数据库极为相似,都支持标准的SQL语

Android数据库高手秘籍(二):创建表和LitePal的基本用法

上一篇文章中我们学习了一些Android数据库相关的基础知识,和几个颇为有用的SQLite命令,都是直接在命令行操作的.但是我们都知道,数据库是要和程序结合在一起使用的,单独对一个数据库去进行増删改查操作并没有什么意义,因此今天我们就来学习一下如何在Android程序当中去操作SQLite数据库,还没看过前一篇文章的朋友可以先去参考Android数据库高手秘籍(一)--SQLite命令 . 操作数据库的第一步当然是创建表了,传统创建表的方法相信大多数人都知道,那么今天我除了会展示传统的建表方法之

基础篇:6.Android数据库编程---SQLite

简介: 在Android平台上,继承了一个嵌入式关系型数据库---SQLite.SQLite具有跨平台特性,可以在所有主要的操作系统上运行.SQLite通过独占性和共享锁实现独立事务处理,因此多个进程可以在同一时间从同一数据库读取数据,但只有一个可以写入数据,在进行写操作之前,必须先获得独占锁.另一方面,SQLite采取动态数据类型,当某个值插入数据库时,SQLite会检查它的类型,如果该类型与所关联的列不匹配,SQLite则会进行强制转换.SQLite支持以下几种数据类型:NULL(空值).I

Android内部自带的SQLite数据库操作dos命令

1:什么叫做SQLite数据库 Android系统内核是Linux系统,Android系统很特殊,他自带了一个SQLite数据库,轻量型的一款嵌入式的数据库 它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了.操作简单,你如果是之前学过其他的数据库的基础的话, 操作起来可以说是举手之劳. 2:这个SQLite数据库的数据类型有哪些. Integer  整型  有符号整数 -1,0,1,2,3- Real 浮点型以 8字节 IEEE 浮点数存储. Text. 值是文本字符串,使用数据

Android数据库SQLite表内设置外键

Android数据库SQLite表内设置外键 介绍 Android默认的数据是SQLite,但SQLite3.6.19之前(在2.2版本中使用的是3.6.22,因此如果你的应用只兼容到2.2版本就可以放心使用外键功能)是不支持外键的,如果有两张表需要关联,用外键是最省事的,但不支持的话怎么办呢?这里就有一个解决办法,就是用事务将两张表关联起来,并且最后生成一张视图. 现有两张表 Employees Dept 视图 ViewEmps:显示雇员信息和他所在的部门 创建数据库 自定义一个辅助类继承SQ