Sqlite3之C++开发常用API总结

2023-11-02

Sqlite3之C++开发常用API总结

注:此篇博客摘抄总结于几个博客,尽可能全面地讲解C++开发过程中涉及的API相关的知识。

使用流程

要使用sqlite,需要从sqlite官网下载到三个文件,分别为sqlite3.lib,sqlite3.dll,sqlite3.h,然后再在自己的工程中配置好头文件和库文件,同时将dll文件放到当前目录下,就完成配置可以使用sqlite了。引用例子如下:

extern "C"
{
  #include "./sqlite3.h" 
  /*
  在 C++ 里使用一段 C 的代码,必须要用 extern “C” 括起来。
  C++ 跟 C虽然语法上有重叠,但是它们是两个不同的东西,内存里的布局是完全不同的,在C++编译器里不用extern “C”括起C代码,会导致编译器不知道该如何为 C 代码描述内存布局。
  可能在 sqlite3.c 里已经把整段代码都 extern “C” 括起来了,但是此处再次声明一次避免意外情况。
  */
};

int main(int, char**)
{
  return 0;
  /*
  sqlite提供的是一些C函数接口,你可以用这些函数操作数据库。
  通过使用这些接口,传递一些标准 sql 语句(以 char * 类型)给 sqlite 函数即可操作数据库。
  */
}

使用的过程根据使用的函数大致分为如下几个过程:

  • sqlite3_open()
  • sqlite3_exec()
  • sqlite3_close()

  • sqlite3_open()
  • sqlite3_prepare()
  • sqlite3_step()
  • sqlite3_column()
  • sqlite3_finalize()
  • sqlite3_close()

这几个过程是概念上的说法,而不完全是程序运行的过程,如sqlite3_column()表示的是对查询获得一行里面的数据列进行的各种操作统称,实际上在sqlite中并不存在这个函数。

sqlite3_open()

在操作数据库之前,首先要打开数据库。这个函数打开一个sqlite数据库文件的连接并且返回一个数据库连接对象。这个操作同时程序中的第一个调用的 sqlite函数,同时也是其他sqlite api的先决条件。许多的sqlite接口函数都需要一个数据库连接对象的指针作为它们的第一个参数。
函数定义:

 int sqlite3_open(

  const char *filename,   /* Database filename (UTF-8) */

  sqlite3 **ppDb          /* OUT: SQLite db handle */

);

int sqlite3_open16(

  const void *filename,   /* Database filename (UTF-16) */

  sqlite3 **ppDb          /* OUT: SQLite db handle */

);

int sqlite3_open_v2(

  const char *filename,   /* Database filename (UTF-8) */

  sqlite3 **ppDb,         /* OUT: SQLite db handle */

  int flags,              /* Flags */

  const char *zVfs        /* Name of VFS module to use */

);

假如这个要被打开的数据文件不存在,则一个同名的数据库文件将被创建。

参数说明:

  • filename:需要被打开的数据库文件的文件名,在sqlite3_open和sqlite3_open_v2中这个参数采用UTF-8编码,而在sqlite3_open16中则采用UTF-16编码。
  • ppDb:一个数据库连接句柄被返回到这个参数,即使发生错误。唯一的一场是如果sqlite不能分配内存来存放sqlite对象,ppDb将会被返回一个NULL值。
  • flags:作为数据库连接的额外控制的参数,可以是SQLITE_OPEN_READONLY,SQLITE_OPEN_READWRITE和SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE中的一个,用于控制数据库的打开方式,可以和SQLITE_OPEN_NOMUTEX,SQLITE_OPEN_FULLMUTEX, SQLITE_OPEN_SHAREDCACHE,以及SQLITE_OPEN_PRIVATECACHE结合使用,具体的详细情况可以查阅文档。

返回值说明:
如果sqlite数据库被成功打开(或创建),将会返回SQLITE_OK,否则将会返回错误码。Sqlite3_errmsg()或者sqlite3_errmsg16可以用于获得数据库打开错误码的英文描述,这两个函数定义为:

const char *sqlite3_errmsg(sqlite3*);

const void *sqlite3_errmsg16(sqlite3*);

Sqlite3_prepare()

这个函数将sql文本转换成一个准备语句(prepared statement)对象,同时返回这个对象的指针。这个接口需要一个数据库连接指针以及一个要准备的包含SQL语句的文本。它实际上并不执行(evaluate)这个SQL语句,它仅仅为准备执行这个sql语句。

函数定义(仅列出UTF-8的):

int sqlite3_prepare(

  sqlite3 *db,            /* Database handle */

  const char *zSql,       /* SQL statement, UTF-8 encoded */

  int nByte,              /* Maximum length of zSql in bytes. */

  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */

  const char **pzTail     /* OUT: Pointer to unused portion of zSql */

);

int sqlite3_prepare_v2(

  sqlite3 *db,            /* Database handle */

  const char *zSql,       /* SQL statement, UTF-8 encoded */

  int nByte,              /* Maximum length of zSql in bytes. */

  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */

  const char **pzTail     /* OUT: Pointer to unused portion of zSql */

);

参数说明:

  • db:数据指针。
  • zSql:sql语句,使用UTF-8编码。
  • nByte:如果nByte小于0,则函数取出zSql中从开始到第一个0终止符的内容;如果nByte不是负的,那么它就是这个函数能从zSql中读取的字节数的最大值。如果nBytes非负,zSql在第一次遇见’/000/或’u000’的时候终止。
  • pzTail:上面提到zSql在遇见终止符或者是达到设定的nByte之后结束,假如zSql还有剩余的内容,那么这些剩余的内容被存放到pZTail中,不包括终止符。
  • ppStmt:能够使用sqlite3_step()执行的编译好的准备语句指针,如果错误发生,它被置为NULL,比如输入的文本不包括sql语句。调用过程必须负责在编译好的sql语句完成使用后使用sqlite3_finalize()删除它。

返回值说明:
如果执行成功,则返回SQLITE_OK,否则返回一个错误码。推荐在现在任何的程序中都使用sqlite3_prepare_v2这个函数,sqlite3_prepare只是用于前向兼容。

备注:

  1. 准备语句(prepared statement)对象
typedef struct sqlite3_stmt sqlite3_stmt;

准备语句(prepared statement)对象一个代表一个简单SQL语句对象的实例,这个对象通常被称为“准备语句”或者“编译好的SQL语句”或者就直接称为“语句”。

语句对象的生命周期经历这样的过程:

  • 使用sqlite3_prepare_v2或相关的函数创建这个对象
  • 使用sqlite3_bind_*()给宿主参数(host parameters)绑定值
  • 通过调用sqlite3_step一次或多次来执行这个sql
  • 使用sqlite3_reset()重置这个语句,然后回到第2步,这个过程做0次或多次
  • 使用sqlite3_finalize()销毁这个对象

在sqlite中并没有定义sqlite3_stmt这个结构的具体内容,它只是一个抽象类型,在使用过程中一般以它的指针进行操作,而sqlite3_stmt类型的指针在实际上是一个指向Vdbe的结构体的指针。

sqlite3_stmt *作为辅助的数据类型:sqlite3_stmt * ,因其已被解析的原因,可以往这个语句里插入二进制数据(也就是操作二进制数据需要使用sqlite3_stmt *)。当然,把二进制数据插到 sqlite3_stmt 结构里可不能直接 memcpy ,也不能像 std::string 那样用 + 号。必须用 sqlite 提供的函数来插入。
注意,要插入二进制,前提是这个表的字段的类型是 blob 类型。

  1. 宿主参数(host parameters)

在传给sqlite3_prepare_v2()的sql的语句文本或者它的变量中,满足如下模板的文字将被替换成一个参数:

  • ?
  • ?NNN,NNN代表数字
  • :VVV,VVV代表字符
  • @VVV
  • $VVV

在上面这些模板中,NNN代表一个数字,VVV代表一个字母数字标记符(例如:222表示名称为222的标记符),sql语句中的参数(变量)通过上面的几个模板来指定,如

“select ? from ? 

“这个语句中指定了两个参数,sqlite语句中的第一个参数的索引值是1,这就知道这个语句中的两个参数的索引分别为1和2,使用”?”的话会被自动给予索引值,而使用”?NNN”则可以自己指定参数的索引值,它表示这个参数的索引值为NNN。”:VVV”表示一个名为”VVV”的参数,它也有一个索引值,被自动指定。
可以使用sqlite3_bind_*()来给这些参数绑定值。

sqlite3_bind()_*()

sqlite3_bind()_*()是一系列值绑定函数,值绑定就是在SQL语句预编译时把一些参数使用占位符(这里是?号)来代替,然后与程序中的变量进行绑定,类似于字符串的格式化。

在sqlite3_prepare_v2()输入的SQL语句的文本中,宿主参数将会被替换掉,说的直白一些,在sqlite3_prepare_v2()中的“?”号就是匿名参数,从第一个问号出现往后的索引默认是1,往后以此类推。而“?NNN”是为匿名参数指定索引,你可以这样写“?1” , "?2"等,而:VVV, @VVV, VVV这些就是有名参数了,VVV就是参数的名字。

//BLOB是数据库中存储大数据的一种数据类型,使用二进制的形式来存储数据
SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));    
 
//绑定double类型数据
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);    
 
//绑定一个32位整型值
SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);
 
//绑定一个64位整型值
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
 
//绑定一个NULL的值(在数据库中可以为NULL)
SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);
 
//绑定一个UTF-8编码的字符串,第四个参数是绑定字符串的长度,如果为负值则传多少绑多少
SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
 
//绑定一个UTF-16编码的字符串,第四个参数是绑定字符串的长度,如果为负值则传多少绑多少
SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));    
 
//绑定sqlite3_value结构体类型的值,sqlite3_value结构体可以保存任意格式的数据
SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);    
 
//绑定一个任意长度的BLOB类型的二进制数据且所有字节被置0,第3个参数是字节长度,其用处是创建一个大的BLOB对象,之后通过BLOB接口函数进行更新
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);

参数说明:

  • sqlite3_bind_*()的第一个参数是含有上述占位符预编译后的语句指针,也就是sqlite3_stmt的对象。

  • sqlite3_bind_*()的第二个参数是SQL语句中参数的索引,例如第一个参数的索引值是1,那么就传1。匿名参数的索引是从1开始往后递增的,而有参数名称的可以通过sqlite3_bind_parameter_index()接口传入参数名称来获取该参数的索引。

  • sqlite3_bind_*()的第三个参数是要绑定的值。

  • sqlite3_bind_blob(), sqlite3_bind_text()和sqlite3_bind_text16()这三个接口中还有有第四和第五个参数,第四个参数代表第三个参数“绑定值”的字节长度。第五个参数是一个指向内存管理回调函数的指针。

返回值说明:
这些绑定函数执行成功后回返回SQLITE_OK, 执行不成功的话回返回相应的错误代码。

sqlite3_bind_parameter_index()用法如下:

int index = sqlite3_bind_parameter_index(statement, "$brandidMin");

第一个参数是sqlite3_stmt的对象,而后边的参数是SQL参数名称,返回值就是该参数的索引。

sqlite3_step()

这个过程用于执行有前面sqlite3_prepare创建的准备语句。这个语句执行到结果的第一行可用的位置。如若继续前进到结果的第二行的话,只需再次调用sqlite3_setp()。持续调用sqlite3_setp()直到这个语句完成,那些不返回结果的语句(如:INSERT,UPDATE,或DELETE),sqlite3_step()只执行一次就返回。

函数定义:

int sqlite3_step(sqlite3_stmt*);

返回值说明:
函数的返回值基于创建sqlite3_stmt参数所使用的函数,假如是使用老版本的接口sqlite3_prepare()和sqlite3_prepare16(),返回值会是 SQLITE_BUSY, SQLITE_DONE, SQLITE_ROW, SQLITE_ERROR 或 SQLITE_MISUSE,而v2版本的接口sqlite3_prepare_v2()和sqlite3_prepare16_v2()则会同时返回这些结果码和扩展结果码。

当进行查询功能时,执行成功的返回值为SQLITE_ROW 而不是 SQLITE_OK,为可以循环执行sqlite3_step 函数,一次step查询出一条记录。直到返回值不为 SQLITE_ROW 时表示查询结束。

对所有V3.6.23.1以及其前面的所有版本,需要在sqlite3_step()之后调用sqlite3_reset(),在后续的sqlite3_ step之前。如果调用sqlite3_reset重置准备语句失败,将会导致sqlite3_ step返回SQLITE_MISUSE,但是在V3.6.23.1以后,sqlite3_step()将会自动调用sqlite3_reset。

int sqlite3_reset(sqlite3_stmt *pStmt);

sqlite3_reset用于重置一个准备语句对象到它的初始状态,然后准备被重新执行(重复使用 sqlite3_prepare 解析好的初始的 sqlite3_stmt 结构)。所有sql语句变量使用sqlite3_bind*绑定值,使用sqlite3_clear_bindings重设这些绑定。Sqlite3_reset接口重置准备语句到它代码开始的时候。sqlite3_reset并不改变在准备语句上的任何绑定值,那么这里猜测,可能是语句在被执行的过程中发生了其他的改变,然后这个语句将它重置到绑定值的时候的那个状态。

值得注意的是,在sqlite3_step (stmt)之前,调用bind绑定的的所有数据必须仍然存在而不能被被释放,否则就不能真正写入,即需要保证bind绑定的内容需要在step调用的时候存在而不被释放,例子如下:

{
  char * tmp_char = (char*)malloc (data_size);
  memcpy(tmp_char, data_buffer, data_size);
  sqlite3_bind_text (stmt, cols, tmp_char, data_size, SQLITE_STATIC);
  free (tmp_char);
  if(SQLITE_DONE == sqlite3_step (stmt)) //写入失败,因为sqlite3_bind_text()绑定的tmp_char已被释放
  {
    sqlite3_reset (stmt);
  }
}
    
{
  char * tmp_char = (char*)malloc (data_size);
  memcpy(tmp_char, data_buffer, data_size);
  sqlite3_bind_text (stmt, cols, tmp_char, data_size, SQLITE_STATIC);
  if(SQLITE_DONE == sqlite3_step (stmt)) //写入成功,因为sqlite3_bind_text()绑定的tmp_char未被释放
  {
    sqlite3_reset (stmt);
  }
  free (tmp_char);
}

sqlite3_column()

这个过程从执行sqlite3_step()执行一个准备语句得到的结果集的当前行中返回一个列。每次sqlite3_step得到一个结果集的列停下后,这个过程就可以被多次调用去查询这个行的各列的值。对列操作是有多个函数,均以sqlite3_column为前缀。

const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);

int sqlite3_column_bytes(sqlite3_stmt*, int iCol);

int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);

double sqlite3_column_double(sqlite3_stmt*, int iCol);

int sqlite3_column_int(sqlite3_stmt*, int iCol);

sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);

const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);

const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);

int sqlite3_column_type(sqlite3_stmt*, int iCol);

sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);

说明:
第一个参数为从sqlite3_prepare返回来的prepared statement对象的指针,第二参数指定这一行中的想要被返回的列的索引。最左边的一列的索引号是0,行的列数可以使用sqlite3_colum_count()获得。

这些过程会根据情况去转换数值的类型,sqlite内部使用sqlite3_snprintf()去自动进行这个转换,下面是关于转换的细节表:

内部类型 请求的类型 转换
NULL INTEGER 结果是0
NULL FLOAT 结果是0.0
NULL TEXT 结果是NULL
NULL BLOB 结果是NULL
INTEGER FLOAT 从整形转换到浮点型
INTEGER TEXT 整形的ASCII码显示
INTEGER BLOB 同上
FLOAT INTEGER 浮点型转换到整形
FLOAT TEXT 浮点型的ASCII显示
FLOAT BLOB 同上
TEXT INTEGER 使用atoi()
TEXT FLOAT 使用atof()
TEXT BLOB 没有转换
BLOB INTEGER 先到TEXT,然后使用atoi
BLOB FLOAT 先到TEXT,然后使用atof
BLOB TEXT 如果需要的话添加0终止符

注:BLOB数据类型是指二进制的数据块,比如要在数据库中存放一张图片,这张图片就会以二进制形式存放,在sqlite中对应的数据类型就是BLOB

int sqlite3_column_bytes(sqlite3_stmt*, int iCol)
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol)

两个函数返回对应列的内容的字节数,这个字节数不包括后面类型转换过程中加上的0终止符。

下面是几个最安全和最简单的使用策略

  • 先sqlite3_column_text() ,然后sqlite3_column_bytes()
  • 先sqlite3_column_blob(),然后sqlite3_column_bytes()
  • 先sqlite3_column_text16(),然后sqlite3_column_bytes16()

sqlite3_exec()

int sqlite3_exec(
  sqlite3* *ppDb,                                /* An open database */

  const char *sql,                               /* SQL to be evaluated */

  int (*callback) (void*, int, char**, char**),  /* Callback function */

  void * parame,                                 /* 1st argument to callback */

  char **errmsg                                  /* Error msg written here */
  
  );

参数说明:

  • ppDb:一个数据库连接句柄参数。
  • sql:一条要执行的 sql 语句,以/0结尾。
  • callback:回调函数,当这条语句执行之后,sqlite3会去调用你提供的这个函数
  • parame:指定任意指针参数,这个参数会自动传给回调函数,如果不需要传递指针给回调函数,可以填NULL。
  • errmsg:错误信息,注意是指针的指针。sqlite3里面有很多固定的错误信息。执行 sqlite3_exec 之后,执行失败时可以查阅这个指针(直接 printf(“%s/n”,errmsg))得到一串字符串信息,这串信息告诉你错在什么地方。sqlite3_exec函数通过修改你传入的指针的指针,把你提供的指针指向错误提示信息,这样sqlite3_exec函数外面就可以通过这个 char*得到具体错误提示。

补充说明:
通常,sqlite3_callback 和它后面的 void * 这两个位置都可以填 NULL。填NULL表示你不需要回调。比如你做insert 操作,做 delete 操作,就没有必要使用回调。而当你做 select 时,就要使用回调,因为 sqlite3 把数据查出来,得通过回调告诉你查出了什么数据。

代码示例:

#include "stdafx.h"
#include "sqlite3.h"

static int callback(void *NotUsed, int argc, char **argv, char **azColName) /*回调函数*/
{
  /*
  第一个参数通过sqlite3_exec的第第四个参数传入的
  第二个参数是结果行的列数
  第三个参数是行中列数据的指针
  第四个参数是行中列名称的指针
  */
    int i;
    for(i = 0; i < argc; i++) {
       printf("%s = %s/n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("/n");
    return 0;
}

#define CHECK_RC(rc, szInfo, szErrMsg, db) if(rc != SQLITE_OK) / /*异常处理*/
{
  printf("%s error!/n", szInfo); /
  printf("%s/n", szErrMsg); /
  sqlite3_free(szErrMsg); /
  sqlite3_close(db); /
  return 0;
}

int _tmain(int argc, _TCHAR* argv[]) /*调用过程*/
{
    sqlite3 *db;
    char *dbPath="f:/test.db";
    char *szErrMsg = 0;


    int rc= sqlite3_open(dbPath, &db);
    CHECK_RC(rc, "open database", db);
    char *szSql = "create table UserInfo(ID int primary key, UserName char, PassWord char);";

    rc = sqlite3_exec(db, szSql, 0, 0, &szErrMsg);
    CHECK_RC(rc, "create table", szErrMsg, db);

    rc=sqlite3_exec(db, "insert into UserInfo(ID, UserName, PassWord) values(1, 'kfqcome', '123456')", 0, 0, &szErrMsg);
    CHECK_RC(rc, "insert info", szErrMsg, db);

    rc=sqlite3_exec(db, "insert into UserInfo(ID,UserName,PassWord) values(2, 'miss wang', '654321')", 0, 0, &szErrMsg);
    CHECK_RC(rc, "insert info", szErrMsg,db);

    szSql="select * from UserInfo";
    rc = sqlite3_exec(db, szSql, callback, 0, &szErrMsg);
    CHECK_RC(rc, "query values", szErrMsg, db);


    sqlite3_close(db);
    getchar();
    return 0;
}

输出的结果:
ID = 1
UserName = kfqcome
PassWord = 123456

ID = 2
UserName = miss wang
PassWord = 654321

这里执行的sql语句用的是sqlite3_exec,它是前面几个函数的封装。
sqlite3_exec是sqlite3_prepare_v2,sqlite3_step()和sqlite3_finalize()的封装,能让程序多次执行sql语句而不要写许多重复的代码。

Sqlite3_exec接口执行0或多个UTF-8编码的,分号分割的sql语句,传到第二个参数中。如果sqlite3_exec的第三个参数回调函数指针不为空,那么它会为每个来自执行的SQL语句的结果行调用(也就是说回调函数会调用多次,上面例子中会返回2个结果行,因而会被执行2次),第4个参数是传给回调函数的第一个参数,如果回调函数指针为空,那么回调不会发生同时结果行被忽略。

如果在执行sql语句中有错误发生,那么当前的语句的执行被停止,后续的语句也被跳过。第五个参数不为空的时候,它被分配内存并写入了错误信息,所以在sqlite3_exec后面需要调用sqlite3_free去释放这个对象以防止内存泄露。

sqlite3_finalize

int sqlite3_finalize(sqlite3_stmt *pStmt);

这个过程销毁前面被sqlite3_prepare创建的准备语句,即sqlite3_stmt对象的析构操作,每个准备语句都必须使用这个函数去销毁以防止内存泄露。

在空指针上调用这个函数没有什么影响,同时可以准备语句的生命周期的任一时刻调用这个函数:在语句被执行前,一次或多次调用sqlite_reset之后,或者在sqlite3_step任何调用之后不管语句是否完成执行。

sqlite3_close

int sqlite3_close(sqlite3 **ppDb );

这个过程关闭前面使用sqlite3_open打开的数据库连接,任何与这个连接相关的准备语句必须在调用这个关闭函数之前被释放。

代码示例:

void MyTest::Add_Data() //该例子由我的同事zkh(EnName:snake)提供
{
    sqlite3_exec(db, "begin;", 0, 0, 0); //使用("begin;")作为参数处理大批量数据的操作语句

    sqlite3_stmt *pstmt;
    string strsql;                                                                //char sql[1024] = "insert into Table_Menu values(?,?);";
    strsql.format("insert into Table_Menu values('%s', ? , ? );", g_str.c_str()); //三列,g_str为有对应意义的全局变量

    ret = sqlite3_prepare_v2(db, strsql, strlen(strsql), &pstmt, NULL);
    if (ret == SQLITE_OK)
    {
        for (map<string, string>::iterator it = mapAllData.begin(); it != mapAllData.end(); it++)
        {

            string strIndex = it->first.c_str();
            string strText = it->second.c_str();
            strIndex = ASCII2UTF8(strIndex);
            strText = ASCII2UTF8(strText);

            sqlite3_bind_text(pstmt, 1, strIndex.c_str(), -1, NULL);
            sqlite3_bind_text(pstmt, 2, strText.c_str(), -1, NULL);

            if (sqlite3_step(pstmt) != SQLITE_DONE) //sqlite3_step()调用sqlite3_reset()
            {
                string strTest;
                strTest.format("db prepare fail, errcode[%d], errmsg[%s]\n", ret, sqlite3_errmsg(db));
            }
        }
    }
    else
    {
        string strTest;
        strTest.format("db prepare fail, errcode[%d], errmsg[%s]\n", ret, sqlite3_errmsg(db));
        sqlite3_close(db);
    }

    sqlite3_exec(db, "commit;", 0, 0, 0); //使用("commit;")作为参数提交存储之后的大量数据
    sqlite3_finalize(pstmt); //析构pstmt对象
    sqlite3_close(db);
}

性能优化

SQLite可以进行参数的配置,从而对性能进行调整,参数配置主要通过pragma指令来实现。
比如空间释放、磁盘同步、Cache大小等。

  1. auto_vacuum
PRAGMA auto_vacuum; 
PRAGMA auto_vacuum = 0 | 1;

查询或设置数据库的auto-vacuum标记。

正常情况下,当提交一个从数据库中删除数据的事务时,数据库文件不改变大小。未使用的文件页被标记并在以后的添加操作中再次使用。这种情况下可使用VACUUM命令释放删除得到的空间。

当开启auto-vacuum,当提交一个从数据库中删除数据的事务时,数据库文件自动收缩, (VACUUM命令在auto-vacuum开启的数据库中不起作用)。数据库会在内部存储一些信息以便支持这一功能,这使得数据库文件比不开启该选项时稍微大一些。

只有在数据库中未建任何表时才能改变auto-vacuum标记,试图在已有表的情况下修改会导致报错。

  1. cache_size
PRAGMA cache_size; 
PRAGMA cache_size = Number-of-pages; //建议改为8000

查询或修改SQLite一次存储在内存中的数据库文件页数。每页使用约1.5K内存,缺省默认的缓存大小是2000. 若需要使用改变大量多行的UPDATE或DELETE命令,并且不介意SQLite使用更多的内存的话,可以增大缓存以提高性能。

当使用cache_size pragma改变缓存大小时,改变仅对当前对话有效,当数据库关闭重新打开时缓存大小恢复到缺省大小。 要想永久改变缓存大小,使用default_cache_size pragma.

  1. case_sensitive_like
PRAGMA case_sensitive_like; 
PRAGMA case_sensitive_like = 0 | 1;

LIKE运算符的默认缺省行为是忽略字符的大小写。因此在缺省情况下’a’ LIKE 'A’的值为真。可以通过打开case_sensitive_like pragma来改变这一缺省行为。当启用case_sensitive_like,‘a’ LIKE 'A’为假而 ‘a’ LIKE 'a’依然为真。

  1. count_changes
PRAGMA count_changes; 
PRAGMA count_changes = 0 | 1;

查询或更改count-changes标记。正常情况下INSERT, UPDATE和DELETE语句不返回数据,当开启count-changes,返回插入,修改或删除的行数。返回的行数不包括由触发器产生的插入,修改或删除等改变的行数。

  1. page_size
PRAGMA page_size; 
PRAGMA page_size = bytes;

查询或设置page_size值,只有在未创建数据库时才能设置page-size。页面大小必须是2的整数倍且大于等于512小于等于8192。上限可以通过在编译时修改宏定义SQLITE_MAX_PAGE_SIZE的值来改变。上限的默认值是32768。

  1. synchronous
    如果有定期备份的机制,而且少量数据丢失可接受,用OFF。
PRAGMA synchronous; 
PRAGMA synchronous = FULL; (2) 
PRAGMA synchronous = NORMAL; (1) 
PRAGMA synchronous = OFF; (0)

查询或更改"synchronous"标记的设定。第一种形式(查询)返回整数值。当synchronous设置为FULL(2), SQLite数据库引擎在紧急时刻会暂停以确定数据已经写入磁盘。这使系统崩溃或电源出问题时能确保数据库在重起后不会损坏。FULL synchronous很安全但很慢。当synchronous设置为NORMAL,SQLite数据库引擎在大部分紧急时刻会暂停,但不像FULL模式下那么频繁。NORMAL模式下有很小的几率(但不是不存在)发生电源故障导致数据库损坏的情况。但实际上,在这种情况下很可能你的硬盘已经不能使用,或者发生了其他的不可恢复的硬件错误。设置为synchronous OFF(0)时,SQLite在传递数据给系统以后直接继续而不暂停。若运行SQLite的应用程序崩溃,数据不会损伤,但在系统崩溃或写入数据时意外断电的情况下数据库可能会损坏。另一方面,在synchronous OFF时,一些操作可能会快50倍甚至更多。
在SQLite2中,缺省值为NORMAL.而在SQLite3中修改为FULL.

  1. temp_store
PRAGMA temp_store; 
PRAGMA temp_store = DEFAULT; (0) 
PRAGMA temp_store = FILE; (1) 
PRAGMA temp_store = MEMORY; (2)

查询或更改"temp_store"参数的设置。当temp_store设置为DEFAULT(0),使用编译时的C预处理宏 TEMP_STORE来定义储存临时表和临时索引的位置。当设置为MEMORY(2),临时表和索引存放于内存中。当设置为FILE(1)则存放于文件中。temp_store_directorypragma可用于指定存放该文件的目录。注意当改变temp_store设置,所有已存在的临时表,索引、触发器及视图将被立即删除。

经测试,在类BBS应用上,通过以上调整,效率可以提高2倍以上。

总结于:
http://blog.chinaunix.net/uid-8447633-id-3321394.html
https://blog.csdn.net/kfqcome/article/details/6570495
https://blog.csdn.net/huanggang982/article/details/98167786
https://www.cnblogs.com/ludashi/p/4786830.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Sqlite3之C++开发常用API总结 的相关文章

随机推荐

  • 2022年全国职业院校技能大赛(中职组)网络安全竞赛试题解析-A

    2022年全国职业院校技能大赛 中职组 网络安全竞赛试题 1 总分100分 需要环境可以私信博主 赛题说明 一 竞赛项目简介 网络安全 竞赛共分A 基础设施设置与安全加固 B 网络安全事件响应 数字取证调查和应用安全 C CTF夺旗 攻击
  • StackGAN笔记

    Stack可译做堆叠 就是在GAN上面再放上一个GAN 作者讲述的自己的解决思路 原来难以生成高分辨率的图像 他们分解了这个问题 把生成高分辨率图片这个任务分解成两个更为简单的任务 就是文中说的一个GAN生成大致的形状和颜色 第二个GAN生
  • 自主HttpServer实现(C++实战项目)

    文章目录 项目介绍 CGI技术 概念 原理 设计框架 日志文件 TCPServer 任务类 初始化与启动HttpServer HTTP请求结构 HTTP响应结构 线程回调 EndPoint类 EndPoint主体框架 读取HTTP请求 处理
  • tidymodels-workflow工作流

    在阅读这篇文章前 我强烈建议你先读一下tidymodels入门篇 tidymodels用于机器学习的细节 首先对tidymodels有一个整体的认知 今天主要介绍workflow的用法 workflow可以把你的数据预处理步骤和模型连接起来
  • 深聊性能测试,从入门到放弃之:性能测试基准与阶段

    如何做性能测试 1 引言 2 性能测试内容 2 1 基准测试 2 2 日常压力测试 2 3 峰值压力测试 2 4 容量测试 2 5 稳定性测试 3 性能测试阶段 3 1 测试确认 3 2 确定通过标准 3 3 测试设计 3 4 测试环境准备
  • 【问题解决】后端如何以文件流的形式返回本地资源给前端,提供下载服务

    后端以文件流的形式返回本地资源 文件地址 String path PDFpath File file new File PDFpath 读取生成的PDF文件 InputStream inputStream OutputStream outp
  • python中文主客观分类

    查了很久发现主客观分析的方法很多 但是数据集少的可怜 能直接使用的库更少 好不容易找到一个 收藏一下 Github 页面 https github com liuhuanyong ZhuguanDetection 下载与使用方法 git c
  • ELK之logstash单节点安装

    ELK之logstash单节点安装 最近在搞ELK 写个文章记录分享一下经验 去官网上下载对应版本的logstash安装包 将工具包上传至服务器 1 解压工具包 命令 tar xzvf logstash tar gz 2 配置logstas
  • Pycharm全局搜索窗口关不掉的解决方案 (ctrl shift F)

    Pycharm全局搜索窗口可以ctrl shift F打开 但是找不到直接关闭的按钮 清空搜索内容也关不掉 只能把窗口挪走 再次ctrl shift F也不会打开新的窗口 一度有点烦恼 后来发现 按ESC就行了 按ESC就行了 按ESC就行
  • JAVA基础常见简答题面试题

    1 为什么java是半编译半解释性的语言 java如何实现跨平台 java的编译器先将其编译为class文件 也就是字节码 然后将字节码交由jvm java虚拟机 解释执行 所以很多地方都说 java是一种半编译 半解释执行 的语言 JAV
  • 拓扑排序 topologicalSort

    拓扑排序 topologicalSort 背景知识 图 Graph 顶点和边 vertex and edge 无向图 Undirected Graph 有向图 Directed Graph 有向图的degree 图中的环 基本概念 什么是拓
  • Angular自定义组件实现ngModel双向绑定

    1 自定义组件ts Component selector app custom tree templateUrl custom tree component html styles providers provide NG VALUE AC
  • [系统安全] 四十八.恶意软件分析 (4)Cape沙箱批量提取动态API特征

    终于忙完初稿 开心地写一篇博客 您可能之前看到过我写的类似文章 为什么还要重复撰写呢 只是想更好地帮助初学者了解病毒逆向分析和系统安全 更加成体系且不破坏之前的系列 因此 我重新开设了这个专栏 准备系统整理和深入学习系统安全 逆向分析和恶意
  • JavaScript-内置对象和包装类型

    作为JS面试题中经常被问到的考点 怎么能不好好了解 理解呢 或许标题中的名词我们不是很了解 但是我相信我们一定都实践过 而且不止一次 只不过当时我们或许停留在使用的层次 没有深入了解原理 在此之前的我也是这样的 不过学习知识 果真是 N 1
  • 恋爱与婚姻的区别

    恋爱是走在婚姻的路上 婚姻是恋爱的最好归宿 不是每一对恋人都能走进婚姻的殿堂 也不是每一对夫妻都能白头偕老 恋爱和婚姻不同 恋爱是没上保险的爱情 而婚姻是上了保险的爱情 在一般情况下 一对恋人容易和平相处 而一对夫妻却常常因为一点小事 争论
  • Anaconda命令-网站大全

    Anaconda命令 网站大全 命令行初始化 conda init 在安装好Anaconda之后配置命令行自动激活base环境 支持PowerShell和Cmd 配置pip Anaconda软件源 清华源 pip 网站 pypi 镜像站使用
  • 数据结构(使用尾插法实现单链表)

    1 线性表的链式存储就是单链表 单链表通过一组任意的存储单元来存储线性表的数据元素 逻辑相邻 存储离散 单链表对于每一个链表结点 不但存储自身数据 还开辟了存储一个指向后继结点的指针 2 单链表相比顺序表 优点 解决了顺序表需要大量连续存储
  • 规则引擎Drools使用 第十三篇 Drools 编写规则文件的规范

    我们在进行drl类型的规则文件编写时尽量遵循如下规范 所有的规则文件 drl 应统一放在一个规定的文件夹中 如 rules文件夹 书写的每个规则应尽量加上注释 注释要清晰明了 言简意赅 同一类型的对象尽量放在一个规则文件中 如所有Stude
  • 梅科尔工作室-李朝阳-鸿蒙笔记3

    list组件 列表组件 相当于大盒子里面套小盒子 示例 父子组件 即主 次两个文件之间内容的调用 if else循环 改变组件的渲染状态 判断是否需要改变状态 示例 for循环 开发框架提供循环渲染 ForEach组件 来迭代数组 并为每一
  • Sqlite3之C++开发常用API总结

    文章目录 Sqlite3之C 开发常用API总结 使用流程 sqlite3 open Sqlite3 prepare sqlite3 bind sqlite3 step sqlite3 column sqlite3 exec sqlite3