SQLite比MySQL还要轻量级,官方提供的程序包大约1MB。但是这么小的内容居然也是一个关系型数据库,所以SQLite也很适合作为入门数据库。其中,SQL最基础的功能就是查询了。
2. 详论
2.1. 打开/关闭数据库
使用数据库的第一步当然是打开数据库了。对于C接口来说,通常的实现是提供一个打开的接口,得到一个句柄或者指针,然后通过这个句柄或者指针进行进一步 *** 作,包括关闭数据库 *** 作。SQLite正是这么设计的:
int nRes = sqlite3_open("D:/test.db", &pDB)
if (nRes != SQLITE_OK) {
cout <<"Open database fail: " <<sqlite3_errmsg(pDB)
return 1
}
sqlite3_close(pDB)
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
通过sqlite3_open()这个函数打开数据库文件,如果数据库.db文件不存在,则会创建这个文件文件。一旦创建成功,在程序结束时最好调用sqlite3_close()这个函数进行关闭,否则会内存泄漏。如果需要更加精细的打开数据库文件控制,最好使用sqlite3_open_v2()这个接口:
int flags =
SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_SHAREDCACHE
int nRes = sqlite3_open_v2("D:/test.db", &pDB, flags, nullptr)
1
2
3
1
2
3
sqlite3_open_v2()提供了很多打开数据库文件时的配置,例如如果需要在数据库文件不存在时进行创建,可以再加上SQLITE_OPEN_CREATE。
2.2. 数据查询
如果在可视化管理工具(例如SQLite Expert)中,无论是查询、插入、修改或者删除 *** 作,都是直接输入SQL语句,然后执行运行 *** 作,就可以得到相应的结果。SQLite也有这样的接口sqlite3_exec(),例如我们查询一个表中所有的数据:
char* cErrMsg
int res =
sqlite3_exec(pDB, "select * from metadata", ResultCallBack, 0, &cErrMsg)
if (res != SQLITE_OK) {
cout <<"select fail: " <<cErrMsg <<endl
return false
}
sqlite3_free(cErrMsg)
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
sqlite3_exec()是一个回调函数,每查询到一行数据就调用一次:
int ResultCallBack(void* NotUsed, int argc, char** argv,
char** azColName) {
for (int i = 0i <argci++) {
cout <<azColName[i] <<" = " <<(argv[i] ? argv[i] : "NULL") <<", "
}
cout <<endl
return 0
}
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
如果需要进行更加细粒度的控制,那么一个合适的做法是使用statement(语句)。首先,我们将我们需要的sql语句编译成数据库引擎识别的字节码,这个步骤可以叫做prepare(准备):
sqlite3_stmt* statement = nullptr
int ret = sqlite3_prepare_v2(pDB, sqlStr, -1, &statement, nullptr)
if (ret != SQLITE_OK) {
printf("prepare error ret : %d\n", ret)
return 1
}
1
2
3
4
5
6
1
2
3
4
5
6
接着,就是执行准备好的SQL语句:
while (sqlite3_step(statement) == SQLITE_ROW) {
//...
}
1
2
3
1
2
3
sqlite3_setp()每次查询一行记录,并且返回SQLITE_ROW;知道查询完成,就返回SQLIET_DONE。通过这种方式实现了一种类似迭代器的 *** 作,循环遍历每个查询结果。
最后,我们对每一行查询的结果进行解析:
while (sqlite3_step(statement) == SQLITE_ROW) {
cout <<sqlite3_data_count(statement) <<'\n'
cout <<sqlite3_column_text(statement, 0) <<'\t'
<<sqlite3_column_text(statement, 1) <<'\n'
}
1
2
3
4
5
1
2
3
4
5
sqlite3_column_X是一系列函数,可以直接获取每一行相应列的数据类型。例如获取整型就是sqlite3_column_int。
另外,不要忘了关闭这个语句:
sqlite3_finalize(statement)
1
1
3. 参考
四、自定义SQL函数或聚合函数
sqlite3_value_xxx这组函数是用于SQL函数或聚合函数内来获取SQLite传入回调函数的参数的。(详见sqlite3_create_function函数或sqlite3_create_function_v2函数)
SQLite允许注册具有相同名称的函数,这些相同名称的函数之间的参数个数或文本编码不能相同。SQLite会根据参数个数或文本编码来调用最合适的回调函数。
非负的nArg参数的回调函数比具有负nArg的函数实现更好的匹配。
其中首选文本编码匹配数据库编码的功能是比编码不同的功能更好的匹配。
其中编码差异在UTF16LE和UTF16BE之间的函数是比编码差异在UTF8和UTF16之间的函数更接近的匹配。
SQLite的内置函数可能会被新的应用程序定义的函数重载。
自定义的SQL函数或聚合函数允许调用SQLite的API函数。但是在自定义的SQL函数或聚合函数内不能调用sqlite3_open、sqlite3_open16、sqlite3_open_v2、sqlite3_step、sqlite3_reset等函数。
下面的例子是自定义一个SQL函数将小写字符串转换成大写的。
第一步、实现SQL函数(提供给SQLite的回调函数)
void test_upper(sqlite3_context *ctxtext,int argc,sqlite3_value **argv){ int nInput
char *zInput,*zOutput,*q /* 获取SQLite传递进回调函数中的数据 */
zInput = (char *)sqlite3_value_text(argv[0]) /* 获取SQLite传递进回调函数中数据的长度 */
nInput = sqlite3_value_bytes(argv[0]) /* 根据数据长度来开辟临时内存 */
zOutput = q = (char *)sqlite3_malloc(nInput) /* 循环将小写的字符转换成大写 */
for(*zInput != '\0'zInput++,q++)
{ if(*zInput >= 'a' && *zInput <= 'z')
{
*q = *zInput & 0xdf //将小写转换成大写
} else
{
*q = *zInput
}
} /* 执行结果放在zOutput 中使用sqlite3_result_text 函数传递回SQLite */
sqlite3_result_text(ctxtext,zOutput,-1,sqlite3_free) //重点}
注:这里的argc与argv参数与main(int argc,char ** argv)中的参数功能是一样的。
第二步、注册SQL函数
sqlite3_create_function(db,"TEST_UPPER",1,
SQLITE_UTF8,NULL,&test_upper,NULL,NULL)
第三步、使用SQL函数
SELECT TEST_UPPER(name) FROM table WHERE id = 1
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)