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

最新文章

  1. 如何正确配置Nginx+PHP
  2. [原创]基于rsync算法的目的性改进-RexSync
  3. 第八章 springboot + mybatis + 多数据源(转载)
  4. C++/C互相调用
  5. qt (5.60/5.70) 编译 QOCI 驱动
  6. windows 7 语言切换 Vistalizator
  7. Java之POJO
  8. Spring中的事物管理,基于spring的bean的配置
  9. [POJ1830]开关问题(高斯消元,异或方程组)
  10. Nexus4铃声目录
  11. Android(java)学习笔记114:LayoutInflater和findViewById
  12. MySQL 删除数据库
  13. TCanvas.CopyRect方法中参数CopyMode的意义
  14. 【原创】leetCodeOj --- Binary Search Tree Iterator 解题报告
  15. 【Python 20】BMR计算器4.0(异常处理)
  16. spark查看DF的partition数目及每个partition中的数据量【集群模式】
  17. 【C编程基础】make命令和makefile文件
  18. autoware
  19. MongoDB——学习
  20. 蒙特卡罗方法 python 实现

热门文章

  1. 微信小程序之日期与时间插件
  2. 如何优雅地使用 Stack Overflow
  3. PAT 天梯赛 L1-044. 稳赢 【循环】
  4. EasyUI:获取某个dategrid的所有行数据
  5. 黑苹果Yosemite 10.10.1懒人版完美安装及简单驱动设置
  6. OpenGL核心技术之Shadow Mapping
  7. HTTP与抓包
  8. 一篇文章学会spark-streaming
  9. Java控制语句——分支、循环、跳转
  10. AJAX基础知识点——思维导图