SQLLite (一)基本介绍
随着云和mobile设备的流行,SQLite作为一款开源的轻量级数据库也被越来越广泛地应用。从HTML5到android,ios和win8为代表的mobile平台都对它提供了天然的支持与整合。因此对SQLite的学习也就显得非常重要。从本文开始的一系列文章将展开对sqllite的介绍。
SQLite是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。
注:ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库系统,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。
SQLite,作为一款轻型的数据库,它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的处理速度比他们都快。
不像常见的客户-服务器范例,SQLite引擎不是个程序与之通信的独立进程,而是连接到程序中成为它的一个主要部分。所以主要的通信协议是在编程语言内的直接API调用。这在消耗总量、延迟时间和整体简单性上有积极的作用。整个数据库(定义、表、索引和数据本身)都在宿主主机上存储在一个单一的文件中。它的简单的设计是通过在开始一个事务的时候锁定整个数据文件而完成的。
注:官方网站是:http://www.sqlite. org或者http://www.sqlite .com .cn,能在上面获得源代码和文档。
特性:
1. ACID事务
2. 零配置 – 无需安装和管理配置
3. 储存在单一磁盘
文件中的一个完整的数据库
4. 数据库文件
可以在不同字节顺序
的机器间自由的共享
5. 支持数据库
大小至2TB
6. 足够小, 大致3万行C代码, 250K
7. 比一些流行的数据库在大部分普通数据库操作要快
8. 简单, 轻松的API
9. 包含TCL绑定, 同时通过Wrapper支持其他语言的绑定
10. 良好注释的源代码
, 并且有着90%以上的测试覆盖率
11. 独立: 没有额外依赖
12. Source完全的Open, 你可以用于任何用途, 包括出售它
13. 支持多种开发语言,C, PHP, Perl, Java, C#,Python
SQLite的数据类型
首先你会接触到一个让你惊讶的名词: Typelessness(无类型). 对! SQLite是无类型的. 这意味着你可以保存任何类型的数据到你所想要保存的任何表的任何列中, 无论这列声明的数据类型是什么(只有在一种情况下不是, 稍后解释). 对于SQLite来说对字段不指定类型是完全有效的. 如:
Create Table ex1(a, b, c);
诚然SQLite允许忽略数据类型, 但是仍然建议在你的Create Table语句中指定数据类型. 因为数据类型对于你和其他的程序员交流, 或者你准备换掉你的数据库引擎时能起到一个提示或帮助的作用. SQLite支持常见的数据类型, 如:
CREATE TABLE ex2(
a VARCHAR(10),
b NVARCHAR(15),
c TEXT,
d INTEGER,
e FLOAT,
f BOOLEAN,
g CLOB,
h BLOB,
i TIMESTAMP,
j NUMERIC(10,5)
k VARYING CHARACTER (24),
l NATIONAL VARYING CHARACTER(16)
);
前面提到在某种情况下, SQLite的字段并不是无类型的. 即在字段类型为”Integer Primary Key”时.
后续文章讲基于ios,介绍c风格的sqllite数据库操作。
SQLLite (二) :sqlite3_open, sqlite3_exec, slite3_close
导入SQLLite library并引入头文件
libsqlite3.dylib本身是个链接,在这里它指向libsqlite3.0.dylib。也就是说在这里你添加libsqlite3.dylib和添加libsqlite3.0.dylib其实是添加了同一个文件,没有区别,那为什么要添加libsqlite3.dylib呢?原因在于libsqlite3.dylib总是指向最新的sqlite3动态库,也就是说如果出现了新的动态库(如:libsqlite3.1.dylib)那libsqlite3.dylib将指向这个新的动态库(libsqlite3.1.dylib)而不在是libsqlite3.0.dylib了!所以建议还是要添加libsqlite3.dylib。
注:
On Mac OS X, frameworks are just libraries, packed into a bundle. Within the bundle you will find an actual dynamic library (libWhatever.dylib). The difference between a bare library and the framework on Mac is that a framework can contain multiple different versions of the library. It can contain extra resources (images, localized strings, XML data files, UI objects, etc.) and unless the framework is released to public, it usually contains the necessary .h files you need to use the library.
A library is just that, "a library". It is a collection of objects/functions/methods (depending on your language) and your application "links" against it and thus can use the objects/functions/methods. It is basically a file containing re-usable code that can usually be shared among multiple applications (you don't have to write the same code over and over again).
打开数据库链接sqlite3_open用法
原型:
- int sqlite3_open(
- const char *filename, <span style="color:#009900;"></span>
- sqlite3 **ppDb <span style="color:#009900;"></span>
- );
用这个函数开始数据库操作。需要传入两个参数,一是数据库文件名,比如:比如:E:/test.db. 在ios中可能是:
- NSArray * documentPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
- NSString * dbPath = [[documentPath objectAtIndex:0] stringByAppendingPathComponent:@“test.db”];
文件名不需要一定存在,如果此文件不存在,sqlite会自动建立它。如果它存在,就尝试把它当数据库文件来打开。二是sqlite3**,即前面提到的关键数据结构。这个结构底层细节如何,你不要管它。 函数返回值表示操作是否正确,如果是SQLITE_OK则表示操作正常。相关的返回值sqlite定义了一些宏。具体这些宏的含义可以参考sqlite3.h 文件。里面有详细定义(顺便说一下,sqlite3 的代码注释率自称是非常高的,实际上也的确很高。只要你会看英文,sqlite 可以让你学到不少东西)。
关闭数据库链接sqlite3_close用法
原型:
- int sqlite3_close(sqlite3 *ppDb);
ppDb为刚才使用sqlite3_open打开的数据库链接
执行sql操作sqlite3_exec用法
原型:
- int sqlite3_exec(
- sqlite3* ppDb, <span style="color:#009900;"></span>
- const char *sql, <span style="color:#009900;"></span>
- int (*callback)(void*,int,char**,char**), <span style="color:#009900;"></span>
- void *, <span style="color:#009900;"></span>
- char **errmsg <span style="color:#009900;"> </span>
- );
这就是执行一条sql 语句的函数。 第1个参数不再说了,是前面open函数得到的指针。第2个参数constchar*sql是一条sql 语句,以\0结尾。 第3个参数sqlite3_callback 是回调,当这条语句执行之后,sqlite3会去调用你提供的这个函数。 第4个参数void*是你所提供的指针,你可以传递任何一个指针参数到这里,这个参数最终会传到回调函数里面,如果不需要传递指针给回调函数,可以填NULL。等下我们再看回调函数的写法,以及这个参数的使用。 第5个参数char** errmsg 是错误信息。注意是指针的指针。sqlite3里面有很多固定的错误信息。执行sqlite3_exec 之后,执行失败时可以查阅这个指针(直接cout<<errmsg得到一串字符串信息,这串信息告诉你错在什么地方。sqlite3_exec函数通过修改你传入的指针的指针,把你提供的指针指向错误提示信息,这样sqlite3_exec函数外面就可以通过这个char*得到具体错误提示。
说明:通常,sqlite3_callback 和它后面的void*这两个位置都可以填NULL。填NULL表示你不需要回调。比如你做insert 操作,做delete操作,就没有必要使用回调。而当你做select 时,就要使用回调,因为sqlite3 把数据查出来,得通过回调告诉你查出了什么数据。
exec 的回调
typedef int(*sqlite3_callback)(void*,int,char**,char**); 你的回调函数必须定义成上面这个函数的类型。下面给个简单的例子: //sqlite3的回调函数 //sqlite 每查到一条记录,就调用一次这个回调 int LoadMyInfo(void* para, int n_column, char** column_value, char** column_name);
//para是你在sqlite3_exec 里传入的void*参数通过para参数,你可以传入一些特殊的指针(比如类指针、结构指针), 然后在这里面强制转换成对应的类型(这里面是void*类型,必须强制转换成你的类型才可用)。然后操作这些数据
//n_column是这一条记录有多少个字段(即这条记录有多少列)
//char** column_value 是个关键值,查出来的数据都保存在这里,它实际上是个1维数组(不要以为是2维数组), 每一个元素都是一个char*值,是一个字段内容(用字符串来表示,以\0结尾)
//char** column_name 跟column_value是对应的,表示这个字段的字段名称
实例:
- 1 #include <iostream>
- 2 using namespace std;
- 3 #include "sqlite/sqlite3.h"
- 4 int callback(void*,int,char**,char**);
- 5 int main()
- 6 {
- 7 sqlite3* db;
- 8 int nResult = sqlite3_open("test.db",&db);
- 9 if (nResult != SQLITE_OK)
- 10 {
- 11 cout<<"打开数据库失败:"<<sqlite3_errmsg(db)<<endl;
- 12 return 0;
- 13 }
- 14 else
- 15 {
- 16 cout<<"数据库打开成功"<<endl;
- 17 }
- 18
- 19 char* errmsg;
- 20
- 21 nResult = sqlite3_exec(db,"create table MyTable(id integer primary key autoincrement,name varchar(100))",NULL,NULL,&errmsg);
- 22 if (nResult != SQLITE_OK)
- 23 {
- 24 sqlite3_close(db);
- 25 cout<<errmsg;
- 26 sqlite3_free(errmsg);
- 27 return 0;
- 28 }
- 29 string strSql;
- 30 strSql+="begin;\n";
- 31 for (int i=0;i<100;i++)
- 32 {
- 33 strSql+="insert into MyTable values(null,'heh');\n";
- 34 }
- 35 strSql+="commit;";
- 36
- 37
- 38 nResult = sqlite3_exec(db,strSql.c_str(),NULL,NULL,&errmsg);
- 39
- 40 if (nResult != SQLITE_OK)
- 41 {
- 42 sqlite3_close(db);
- 43 cout<<errmsg<<endl;
- 44 sqlite3_free(errmsg);
- 45 return 0;
- 46 }
- 47
- 48 strSql = "select * from MyTable";
- 49 nResult = sqlite3_exec(db,strSql.c_str(),callback,NULL,&errmsg);
- 50 if (nResult != SQLITE_OK)
- 51 {
- 52 sqlite3_close(db);
- 53 cout<<errmsg<<endl;
- 54 sqlite3_free(errmsg);
- 55 return 0;
- 56 }
- 57
- 58 sqlite3_close(db);
- 59 return 0;
- 60 }
- 61
- 62 int callback(void* ,int nCount,char** pValue,char** pName)
- 63 {
- 64 string s;
- 65 for(int i=0;i<nCount;i++)
- 66 {
- 67 s+=pName[i];
- 68 s+=":";
- 69 s+=pValue[i];
- 70 s+="\n";
- 71 }
- 72 cout<<s<<endl;
- 73 return 0;
- 74 }
SQLLite (三):sqlite3_get_table,sqlite3_free_table
上一篇介绍的sqlite3_exec 是使用回调来执行对select结果的操作,你得声明一个函数,如果这个函数是类成员函数,你还不得不把它声明成static的(要问为什么?这又是C++基础了。C++成员函数实际上隐藏了一个参数:this,C++调用类的成员函数的时候,隐含把类指针当成函数的第一个参数传递进去。结果,这造成跟前面说的sqlite 回调函数的参数不相符。只有当把成员函数声明成static 时,它才没有多余的隐含的this参数)。
有时候你还是想要非回调的select 查询。这可以通过sqlite3_get_table 函数做到。
- 1 int sqlite3_get_table(
- 2 sqlite3 *db, <span style="color:#009900;"></span>
- 3 const char *zSql, <span style="color:#009900;"></span>
- 4 char ***pazResult, <span style="color:#009900;"></span>
- 5 int *pnRow, <span style="color:#009900;"></span>
- 6 int *pnColumn, <span style="color:#009900;"></span>
- 7 char **pzErrmsg <span style="color:#009900;"></span>
- 8 );
- 9 void sqlite3_free_table(char **result);
第1个参数不再多说,看前面的例子。 第2个参数是sql 语句,跟sqlite3_exec 里的sql 是一样的。是一个很普通的以\0结尾的char*字符串。 第3个参数是查询结果,它依然一维数组(不要以为是二维数组,更不要以为是三维数组)。它内存布局是:字段名称,后面是紧接着是每个字段的值。下面用例子来说事。 第4个参数是查询出多少条记录(即查出多少行,不包括字段名那行)。 第5个参数是多少个字段(多少列)。 第6个参数是错误信息,跟前面一样,这里不多说了。
pazResult返回的字符串数量实际上是(*pnRow+1)*(*pnColumn),因为前(*pnColumn)个是字段名
修改上篇的例子,使用sqlite3_get_table,来去的结果集:
- #include <iostream>
- 2 using namespace std;
- 3 #include "sqlite/sqlite3.h"
- 4 int callback(void*,int,char**,char**);
- 5 int main()
- 6 {
- 7 sqlite3* db;
- 8 int nResult = sqlite3_open("test.db",&db);
- 9 if (nResult != SQLITE_OK)
- 10 {
- 11 cout<<"打开数据库失败:"<<sqlite3_errmsg(db)<<endl;
- 12 return 0;
- 13 }
- 14 else
- 15 {
- 16 cout<<"数据库打开成功"<<endl;
- 17 }
- 18
- 19 char* errmsg;
- 20
- 21 nResult = sqlite3_exec(db,"create table MyTable(id integer primary key autoincrement,name varchar(100))",NULL,NULL,&errmsg);
- 22 if (nResult != SQLITE_OK)
- 23 {
- 24 sqlite3_close(db);
- 25 cout<<errmsg;
- 26 sqlite3_free(errmsg);
- 27 return 0;
- 28 }
- 29 string strSql;
- 30 strSql+="begin;\n";
- 31 for (int i=0;i<100;i++)
- 32 {
- 33 strSql+="insert into MyTable values(null,'heh');\n";
- 34 }
- 35 strSql+="commit;";
- 36
- 37
- 38 nResult = sqlite3_exec(db,strSql.c_str(),NULL,NULL,&errmsg);
- 39
- 40 if (nResult != SQLITE_OK)
- 41 {
- 42 sqlite3_close(db);
- 43 cout<<errmsg<<endl;
- 44 sqlite3_free(errmsg);
- 45 return 0;
- 46 }
- 47
- 48 strSql = "select * from MyTable";
- <span style="color:#009900;">49
- 50 char** pResult;
- 51 int nRow;
- 52 int nCol;
- 53 nResult = sqlite3_get_table(db,strSql.c_str(),&pResult,&nRow,&nCol,&errmsg);
- 54 if (nResult != SQLITE_OK)
- 55 {
- 56 sqlite3_close(db);
- 57 cout<<errmsg<<endl;
- 58 sqlite3_free(errmsg);
- 59 return 0;
- 60 }
- 61
- 62 string strOut;
- 63 int nIndex = nCol;
- 64 for(int i=0;i<nRow;i++)
- 65 {
- 66 for(int j=0;j<nCol;j++)
- 67 {
- 68 strOut+=pResult[j];
- 69 strOut+=":";
- 70 strOut+=pResult[nIndex];
- 71 strOut+="\n";
- 72 ++nIndex;
- 73 }
- 74 }
- 75 sqlite3_free_table(pResult);
- 76 cout<<strOut<<endl;
- 77 sqlite3_close(db);
- 78 return 0;
- 79 }
- <span style="color:#009900;">80
-
-
-
-
-
-
-
-
-
-
-
-
- </span>
SQLLite (三):sqlite3_prepare_v2,sqlite3_step
如果既不想写回调函数,又想避免
sqlite3_get_table之后麻烦
的一维数组遍历,那么利用sqlite3_prepare_v2执行sql select语句,让后sqlite3_step遍历select执行的返回结果是一个非常方便的solution. 当然,你必须要明白sqlite3_prepare_v2不仅仅能够执行table的query selection,也能方便地进行sql Delete, Insert, Update等其他一些操作。它能帮你把sql语句的执行操作变的更加优雅。
- int sqlite3_prepare_v2(
- sqlite3 *db, <span style="color:#009900;"></span>
- const char *zSql, <span style="color:#009900;"></span>
- int nByte, <span style="color:#009900;"></span>
- sqlite3_stmt **ppStmt, <span style="color:#009900;"></span>
- const char **pzTail <span style="color:#009900;"></span>
- );
- int sqlite3_step(sqlite3_stmt*);
下面以一段iOS中的selection查询为例说明二者的用法:
- -(void)InitializeFilesTable
- {
- const char * query = "SELECT * FROM [FileObjects]";
- sqlite3_stmt * stmt;
- int result = sqlite3_prepare_v2(mDatabase, query, -1, &stmt, NULL);
- if(SQLITE_OK != result)
- {
- sqlite3_finalize(stmt);
- <span style="color:#009900;">
-
- char * sql = "Create TABLE [FileObjects] ([FileId] VARCHAR(128),[FileExt] VARCHAR(128), [FileName] VARCHAR(128), [FileUrl] VARCHAR(128), [FileType] INT );"; <span style="color:#009900;">
- char * errMsg;
- result = sqlite3_exec(mDatabase, sql, NULL, NULL, &errMsg);
- }
- else
- {
- <span style="color:#009900;">
- NSMutableDictionary * files = [NSMutableDictionary new];
- while(sqlite3_step(stmt) == SQLITE_ROW)
- {
- FileObject * file = [FileObject new];
-
- const char * str = (const char *)sqlite3_column_text(stmt, 0);
- file.FileId = str? [[NSString alloc] initWithUTF8String:str] : @"";
-
- str = (const char *)sqlite3_column_text(stmt, 1);
- file.FileExt = str? [[NSString alloc] initWithUTF8String:str] : @"";
-
- str = (const char *)sqlite3_column_text(stmt, 2);
- file.FileName = str? [[NSString alloc] initWithUTF8String:str] : @"";
-
- str = (const char *)sqlite3_column_text(stmt, 3);
- file.FileUrl = str? [[NSString alloc] initWithUTF8String:str] : @"";
-
- file.FileType = sqlite3_column_int(stmt, 4);
-
- [files setObject:file forKey:file.FileId];
- }
-
- sqlite3_finalize(stmt);
- [mFiles setDictionary:files];
- }
- }
这其中包括对sqlite3_exec的调用。sqlite3_exec可以执行任何sql语句,包括事务(
"BEGIN TRANSACTION"
)、回滚(
"ROLLBACK")和提交(
"COMMIT")等等。
转自:http://blog.csdn.net/lyrebing?viewmode=contents