0. 前言
最近小蕊同学工作中遇到了在后端处理时调用sqlite的问题,于是做一点简单的总结。
1. 安装
sqlite不需要进行复杂的安装,但是为了后续项目需要,可以将源码先编译成静态链接库。
具体细节可以参照[1]中作者所给出的步骤,在这里只做简单的总结。
1.1
首先是在官网[2]中下载sqlite-amalgamation-3290000.zip (Source code), sqlite-dll-win64-x64-3290000.zip (Precompiled for windows) , sqlite-tools-win32-x86-3290000.zip
amalgamation是源码,稍后编译为静态链接库
sqlite-dll 是预编译的动态链接库
sqlite-tools 是预编译的数据库操作工具,如果只需要在dos窗口下用SQL简易操作,则仅需要下载使用这个,我们的目标是在项目中(based on C)使用SQL来操作,因此暂时先将三个文件一块儿下载了。
1.2
下载完毕后,在VS2017中建立一个项目,用来编译静态库。
新建项目 --> Visual C++ --> Windows 桌面 --> 静态库或者动态库
然后将刚刚下载的sqlite.c, sqlite.h, sqlite.dll, sqlite.def 拷贝到项目目录下再添加至项目中去(其实好像dll没啥用在这里)
编译那儿选择x64(根据需求来)
然后选择
属性 --> C/C++ --> 预编译头 --> 不使用预编译头
属性 --> 常规 --> 配置类型 --> 静态库(.lib)
搞定后,在主界面选择 生成 --> 生成sqlite(U)
2. SQL基本语句
这里总结的不全面,基本上是小蕊同学工作上遇到的,都挺基础。可以直接从刚刚下载的tool包里打开sqlite.exe,在dos窗口下open一个数据库,“.open test.db”(不存在就是创建数据库)
create table NAME
(ID int primary key not null,
NAME varchar(10) not null,
X int,Y int,Z int);
insert into TABLE values(X1,X2,X3,X4,X5);
select ID, NAME from TABLE
where XXX > XXX and YYY < YYY;
update TABLE
set XXX=xxx, YYY=yyy where ID = ZZZ;
delete from TABLE where ID = ZZZ;
select * from TABLE
order by XXX ASC|DESC;
比较复杂的语句暂时没有遇到,时刻记住select 返回的是一张表就行了,这样就不怕select嵌套啦
3. C语言里调用
3.1 常用的函数与流程
在incude后加载lib
#pragma comment(lib, "sqlite_compile_LIB.lib")
打开/关闭数据库(不存在就是创建)
sqlite3 *db = NULL;
int rc = sqlite3_open("test.db", &db);
sqlite3_close(db);
直接执行语句,在db中执行SQL_str的内容,就是SQL语句啦
rc = sqlite3_exec(db, SQL_str, NULL, NULL, NULL);
事务处理:
在sqlite中,若直接执行某一语句(例如INSERT),会默认为一次事务: begin transaction --> insert --> commit transaction
这样是非常消耗时间的,在批处理中,我们将多次操作显式地整合为一次事务:
例如:
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
for i=1-->100000
sqlite3_exec(XXXX);
rc = sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
然而,当我们调用sqlite3_exec函数时,它每一次都会编译一遍SQL_str中的SQL语句,往往这些语句时重复的
例如重复地做insert到某一张table的操作。 大量的重复编译会消耗60%左右的时间。
因此,我们可以采用预编译(绑定bind)来实现只编译一次,重复使用的操作
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
sqlite3_stmt *stmt; // stmt = 'statement'
const char* sql = "INSERT INTO STUDENTS VALUES(?,?,?,?)";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0); //预编译,现在 stmt就代表"INSERT INTO STUDENTS VALUES(?,?,?,?)"
for i=1-->1000000
sqlite3_reset(stmt);
sqlite3_bind_int/sqlite3_bind_text/ ... ...
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
rc = sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
3.2 例子
新建一个项目,将刚刚编译的.lib, .h, .c, .dll 一股脑的导入进去,也设置为x64,不预编译头
//#include <iostream>
#include "sqlite3.h"
#pragma comment(lib, "sqlite_compile_DLL.lib")
#include <stdio.h>
#include<time.h>
#include<string.h>
void test_is_install()
{
sqlite3 *db = NULL;
char *error_msg = NULL;
int rc = sqlite3_open("test.db", &db);
if (rc)
{
printf("SQL error: %s \n", sqlite3_errmsg(db));
}
else
{
printf("db is opened \n");
}
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
rc = sqlite3_exec(db, "CREATE TABLE STUDENTS(NUMBER VARCHAR(10), NAME VARCHAR(10), SEX VARCHAR(6), AGE VARCHAR(3));", NULL, NULL, NULL);
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00001', 'SUE', 'FAMALE', '10');", NULL, NULL, NULL);
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00002', 'SANDY', 'MALE', '11');", NULL, NULL, NULL);
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00003', 'TOMMY', 'MALE', '11');", NULL, NULL, NULL);
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00004', 'SALLY', 'FAMALE', '13');", NULL, NULL, NULL);
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00005', 'SODA', 'MALE', '8');", NULL, NULL, NULL);
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00006', 'MARY', 'FAMALE', '14');", NULL, NULL, NULL);
rc = sqlite3_exec(db, "SELECT STUDENTS.* FROM STUDENTS WHERE SEX='MALE';", NULL, NULL, NULL);
rc = sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
printf("error code: %d \n", rc);
printf("error msg: %s \n", error_msg);
sqlite3_close(db);
}
void transaction_batch_insert()
{
// 初始化
sqlite3 *db = NULL;
char *error_msg = NULL;
int rc = sqlite3_open("batch_insert_using_transaction.db", &db); //打开、创建数据库
sqlite3_exec(db, "PRAGMA synchronous = OFF; ", 0, 0, 0);
if (rc)
{
printf("SQL error: %s \n", sqlite3_errmsg(db));
}
else
{
printf("db is opened \n");
}
int batch = 30000;
clock_t start, end;
start = clock(); //计时开始
//开启事务(Transaction)
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
//创建表格 Students
rc = sqlite3_exec(db, "CREATE TABLE STUDENTS(NUMBER VARCHAR(10), NAME VARCHAR(10), SEX VARCHAR(6), AGE VARCHAR(3));", NULL, NULL, NULL);
for (int i = 1; i <= batch; i++) //30000次
{
//逐条插入数据
rc = sqlite3_exec(db, "INSERT INTO STUDENTS VALUES('00001', 'SUE', 'FAMALE', '10');", NULL, NULL, NULL);
}
//提交事务(Transaction)
rc = sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
end = clock();
printf("time=%f\n", (double)(end - start) / 1); //CLOCKS_PER_SEC
printf("time=%f\n", (double)(end - start) / CLOCKS_PER_SEC);
sqlite3_close(db);
}
void precompile_insert()
{
/* 1. 使用上述transaction_batch_insert()函数的事务处理机制
2. 将复用的SQL语句预编译,以达到不用重复编译的目的
*/
sqlite3 *db = NULL;
char *error_msg = NULL;
int rc = sqlite3_open("batch_insert_using_precompile.db", &db);
//sqlite3_exec(db, "PRAGMA synchronous = OFF; ", 0, 0, 0);
if (rc)
{
printf("SQL error: %s \n", sqlite3_errmsg(db));
}
else
{
printf("db is opened \n");
}
int batch = 3;
clock_t start, end;
start = clock();
rc = sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
rc = sqlite3_exec(db, "CREATE TABLE STUDENTS(NUMBER VARCHAR(10), NAME VARCHAR(10), SEX VARCHAR(6), AGE VARCHAR(3));", NULL, NULL, NULL);
sqlite3_stmt *stmt; // stmt = 'statement'
const char* sql = "INSERT INTO STUDENTS VALUES(?,?,?,?)";
sqlite3_prepare_v2(db, sql, strlen(sql), &stmt, 0); //预编译,现在 stmt就代表"INSERT INTO STUDENTS VALUES(?,?,?,?)" 啦
for (int i = 1; i <= batch; i++)
{
sqlite3_reset(stmt);
rc = sqlite3_step(stmt);
printf("rc=%d \n", rc);
sqlite3_bind_text(stmt, 1, "00001", 6, NULL);
sqlite3_bind_text(stmt, 2, "Sue", 4, NULL);
sqlite3_bind_text(stmt, 3, "F", 2, NULL);
sqlite3_bind_text(stmt, 4, "12", 3, NULL);
rc = sqlite3_step(stmt);
printf("rc=%d \n", rc);
}
sqlite3_finalize(stmt);
rc = sqlite3_exec(db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
end = clock();
printf("time=%f\n", (double)(end - start) / 1); //CLOCKS_PER_SEC
printf("time=%f\n", (double)(end - start) / CLOCKS_PER_SEC);
printf("error code: %d \n", rc);
printf("error msg: %s \n", error_msg);
sqlite3_close(db);
}
int main()
{
//transaction_batch_insert(); //400ms
precompile_insert(); //192.6ms
return 0;
}
4. Reference
[1] https://blog.youkuaiyun.com/jiangwei0910410003/article/details/79435074