sqlite数据库的基本操作

今天来聊一聊sqlite数据库的一些基本操作, 之前一直用的是FMDB来操作数据库的, 但是也想对sqlite本身也做一个了解, 那我们就来一起学习一下.

sqlite数据库是一种轻量级数据库,被集成到用户程序中, 占用资源非常的低,在嵌入式设备中,占用内存很低而查询效率很高,因此广泛应用在各种移动客户端中;
简单,轻巧的足够小(C语言API ,4.43M)
快(比大部分的普通数据库都快)
支持的数据库大小至2TB,所以注定了它是一个跨平台利器,在Android与iOS下均能使用,而且完全可以写出通用的代码,方便我们移植。最后一个重要的原因就是原生的使用也是相当简单
Demo的操作环境: Xcode 8.3.3


1. 导入

一图流:
sqlite数据库的基本操作_第1张图片
忽略我的警告… :P
然后在你需要使用sqlite3的地方导入头文件

#import 

喏, 然后就可以愉快的开始使用了~


2. 我想打开一个数据库!

下面就是sqlite3定义的打开数据库的方法:

int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

BTW: 我能说第一次我看到这个方法的时候以为是要手动写的, 自己定义了一套么.. :(

上面第一个参数就是数据库所存放的路径,如果有就直接用, 如果路径下没有数据库文件则系统会在此路径下创建一个数据库。至于第二个参数则是数据库的句柄引用,但此函数调用成功后此句柄将会保存打开数据库的句柄,此句柄在往后的数据库操作中需要用到。因此,可如下方式调用:

NSString *path = [NSString stringWithFormat:@"%@/Documents/demo.db",NSHomeDirectory()];
    if (sqlite3_open([path UTF8String], &_dbHandle)==SQLITE_OK) {
        NSLog(@"打开数据库成功!");
    }

来解释一下句柄:
句柄在很多地方我们见到过,最常见的就是文件的句柄,我们要操纵一个文件,我们就要取得一个文件的句柄。句柄是个什么东西?其实很简单,句柄是一个东西的描述,他被定义为一个结构体,这个结构体可能会包含要描述的东西的具体信息,比如位置、大小、类型等等。我们有了这个描述信息我们就能去找到这个东西,然后操纵它。一句话:句柄是物体的描述结构体
咱还可以理解为“把手” : 虽然你握住的只是把手,却能拉动整扇门,而且你根本不用在意那门长什么样子, 一扇门如果有多个把手,被不同的人(进程)握住,门往哪儿走就不好说了…
这是我定义的一个句柄:

static sqlite3 *dbHandle;

3. 关闭数据库!

既然有打开数据库,那么就肯定有关闭数据库的操作了,我们要养成良好的习惯,当需要使用数据库的时候就执行sqlite3_open来打开数据库,等使用完数据库后就调用sqlite3_close函数来对数据库进行关闭。关于sqlite3_close函数声明如下:

int sqlite3_close(sqlite3 *);

关闭的时候把我们定义的句柄传入数据库即可对数据库进行关闭。调用方式如下:

if (sqlite3_close(_dbHandle)==SQLITE_OK) {
        NSLog(@"关闭数据库成功!");
    }else{
        NSLog(@"error %s",sqlite3_errmsg(_dbHandle));
    }

4. 数据库的两种基本操作

4.1 sqlite3_exec函数

常用的有两种方式可以操作数据库中的数据和调整结构, 第一种就是sqlite3_exec函数,这种方法一般用在增、删、改。这个函数不适合来查询数据库, 后面我们再介绍查询数据库的方法. 先来看看此方法的声明:

int sqlite3_exec(
  sqlite3*,                                  /* An open database 这是我们打开数据库的句柄*/
  const char *sql,                           /* SQL to be evaluated 待执行的SQL语句*/
  int (*callback)(void*,int,char**,char**),  /* Callback function 回调函数,有固定的格式,你可以自己实现*/
  void *,                                    /* 1st argument to callback 回调函数的第一个参数*/
  char **errmsg                              /* Error msg written here 如果方法执行后存在异常,那么这个参数保存的就是错误的描述信息*/
);

4.2 创建数据表

要想让数据库能够存储数据,那就必须得创建一个数据表才能进行数据操作。而数据表是可以包含不同的数据字段,这些字段可以指定不同的数据类型,存储不同的数据。我们建表时可以根据需要进行创建。下面的代码创建了一个叫做persons的数据表,其中包含了3个字段id、name和age. (如果对SQL语句不太熟悉的朋友可以参考相关方面资料), 写个实例来演示上面的sqlite3_exec函数:

-(void)createTable
{
    //因为sqlite3是C写的,所以传入的时候,sqlite语句要用C的形式传入,如果用NSString *createString = ***; 这种形式会报错
    const char *createString = "create table if not exists persons (id integer primary key autoincrement, name varchar(256), age integer)";
    char *errorMsg;
    BOOL result = sqlite3_exec(_dbHandle, createString, nil, nil, &errorMsg);
    if (result == SQLITE_OK) {
        NSLog(@"建表成功!");
    }else{
        NSLog(@"建表失败!Error:%s",sqlite3_errmsg(_dbHandle)));
    }
    sqlite3_free(errorMsg);
}

面要注意的一点是,如果你有传入errorMsg参数,那么你必须在执行完sqlite3_exec后,执行sqlite3_free函数来释放errorMsg。否则就会造成内存泄露

有个需要注意的点:

先看两张图:
sqlite数据库的基本操作_第2张图片

sqlite数据库的基本操作_第3张图片
然后你猜猜会报什么错? ;)

2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 打开数据库成功!
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 关闭数据库成功!
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 建表失败!Error:(null)
2017-06-22 17:44:53.764 SQLiteDemo[68053:2703014] 插入数据失败! Error:library routine called out of sequence

不明所以? 一脸蒙蔽? 恩…当时的我就是这个样子的! 为什么是null? 如果出了错都不知道原因是什么… :o
所以我用了第二种sqlite3_errmsg(_dbHandle)来打印错误信息,这是一个报错的函数,所以结果就很清楚了: 库程序调用顺序问题

4.3 增 ! 删 ! 改 !

  • 4.3.1 插入数据
    上面的例子创建了一个数据后,那么我们可以往里面插入数据,我们可以使用insert语句将数据插入表中:代码如下所示:
-(void)insertInfo
{
    const char *insert = "insert into persons (name,age) values ('阿童木',5)";
    char *errorMsg;
    BOOL result = sqlite3_exec(_dbHandle, insert, nil, nil, &errorMsg);
    if (result == SQLITE_OK) {
        NSLog(@"插入数据成功!");
    }else{
        NSLog(@"插入数据失败! Error:%s",sqlite3_errmsg(_dbHandle));
    }
}

打印输出:

2017-06-22 17:39:56.379 SQLiteDemo[67920:2698580] 插入数据成功!
  • 4.3.2 删除数据
    删除数据只是SQL语句的语法不同而已,其他都一样, 提供两种删除方式:
delete from persons; //会直接删除所有的数据,但是不会清空自动增长列的标识。
delete from persons where id = 8;  //根据id删除
-(void)deleteInfo
{
    const char *delete = "delete from persons where id = 1";

    BOOL result = sqlite3_exec(_dbHandle, delete, nil, nil, &errorMsg);

    if (result == SQLITE_OK) {
        NSLog(@"删除数据成功!");
    }else{
        NSLog(@"删除数据失败!Error:%s",sqlite3_errmsg(_dbHandle));
    }
}
  • 4.3.3 更改数据
    同上, 其他都一样

    const char *change = "update persons set name = '铁甲小宝' age = '40' where id = 2";

4.4 重点: 查 !

  • sqlite3_prepare_v2和sqlite3_step函数

我们查询数据库就使用sqlite3_prepare_v2和sqlite3_step两个函数搭配进行操作。其中sqlite3_prepare_v2是一个将SQL语句编译为sqlite内部一个结构体(sqlite3_stmt).该结构体中包含了将要执行的的SQL语句的信息。而sqlite3_step则是让转化后的SQL进行下一步的操作。因此通过这两个函数可以很方便的获取到数据库中的数据。本人建议使用此方式取得记录集。下面是这两个函数的声明:

int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle  句柄*/
  const char *zSql,       /* SQL statement, UTF-8 encoded  SQL语句 */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);

第三个参数是用于指定SQL语句最大的长度,如果此参数为负数,则根据第二个参数中的第一个终结符为准作为一条完整的语句。如果为非负数,则以第二个参数的第一个终结符,或者指定的数字为准作为一条完整语句。第四个参数则是调用函数后返回的一个结构体,此结构体包含了相关语句的信息。关于第五个参数是用于指向前一条语句结束位置,一旦指定此参数,则参数指向位置的左边语句将不进行编译解析。

sqlite3_step(sqlite3_stmt *);

传入参数即为准备语句中的结构体对象。
例子:

-(void)searchInfo
{
    //创建数组 存数据
    NSMutableArray *array = [[NSMutableArray alloc]init];

    const char *select = "select * from persons where name = '阿童木'";

    //sqlite3_prepare_v2的参数第一个是数据库连接,第二个是sql语句,第三个是这个语句的长度传入-1表示到第一个null终止符为止,第四个是返回一个语句对象,第五个是返回一个指向该sql语句的第一个字节的指针
    int result = sqlite3_prepare_v2(_dbHandle, select, -1, &stmt, nil);
    if (result == SQLITE_OK) {
        // 开始遍历 每调一次sqlite3_step函数,stmt就会指向下一条记录
        while (sqlite3_step(stmt) == SQLITE_ROW) {  

            int ID = sqlite3_column_int(stmt, 0);  // 取出第0列字段的值(int类型的值)
            const unsigned char *name = sqlite3_column_text(stmt, 1);  // 取出第1列字段的值(text类型的值)
            int age = sqlite3_column_int(stmt, 2);  // 取出第2列字段的值(int类型的值)
            //将C字符串转换为OC字符串
            NSString *name1 = [[NSString alloc]initWithUTF8String:(const char *)name];  
            //将model实例化 并把取出来的数据存入model
            ZTPerson *p = [[ZTPerson alloc]init];
            p.name = name1;
            p.age = age;
            p.ID = ID;
            [array addObject:p];
        }
    }else{
        NSLog(@"查询语句有问题,查询失败!");
    }
    NSLog(@"%@",array);
}

这就是今天的关于sqlite数据库的内容, 感觉还差点什么, 明天想起来再加进去.
2017年06月22日22:01:38 Reborn_Tai

你可能感兴趣的:(数据库)