iOS平台sqlite数据库的使用-King

在iOS开发中经常需要对数据进行存储,平常我们进行简单的数据存储会用到plist存储,或者偏好设置的值,用户账户密码等比较单一的数据使用NSUserDefaults ,但是遇到比较复杂的,数量比较大得数据,用前两种方法不是很合适.iOS平台提供了一种轻量级的数据存储方式sqlite数据库进行存储数据.


        那么什么是sqlite?sqlite是一款轻量型的嵌入式数据库,,它占用资源非常少,在嵌入式设备中,可能只需要几百K的内存就够了.它的处理速度比Mysql、PostgreSQL这两款著名的数据库都还快.

        我们经常使用的关系型数据库,比如:Oracle、MySQL、SQL Server、Access、DB2、Sybase ,但是在嵌入式/移动客户端有sqlite.

关系型数据库是数据库的一种,数据库主要分两大类,关系型数据库和对象型数据库.

        数据库的存储结构和excel很像,以表为单位.创建一张表之后,然后可以在创建好的表中添加多个字段(column,列,属性),然后就可以添加多行记录了(row,每行存放多个字段对应的值).sqlite语句主要就是四个字,增删改查.并且不区分大小写,每条语句都以分号结尾,不能以关键字命名表明和字段.

        sqlite语句中常用的关键字有:select、insert、update、delete、from、create、where、desc、order、by、group、table、alter、view、index 等等.

        

sqlite语句的种类:

        数据定义语句(DDL:Data Definition Language )

                包括createdrop等操作 

                在数据库中创建新表或删除表(create table或 drop table


        数据操作语句(DML:Data Manipulation Language )

                包括insertupdatedelete等操作 

                上面的3种操作分别用于添加、修改、删除表中的数据


        数据查询语句(DQL:Data Query Language )

                可以用于查询获得表中的数据 

                关键字select是DQL(也是所有SQL)用得最多的操作 

                其他DQL常用的关键字有where,order by,group byhaving


创表:

格式:    

            create table 表名(字段名1 字段类型1,字段名2 字段类型2,...);

            create table if not exists 表名(字段名1 字段类型1,字段名2 字段类型2,...);

举例:     

 
 
  1. create table if not exists t_myCode(id interger,name text,age text);

在上述示例中,integer等是数据存储类型

        在sqlite中数据划分为以下几种类型:

        integer,real,text,blob(整型,浮点型,文本字符串,二进制文件)

        但是在sqlite中,实际上是没有数据类型的划分的,即使声明为integer类型,也能存储blob类型的文件或者text类型的字符串,但是主键除外,建表时,实际上是可以不声明数据类型的,但是这不是一个良好的编程习惯,也许以后你自己就会忘了,这个字段你需要存储什么类型的文件,也不方便其他程序员阅读你的代码,所以在建表是最好加上每个字段的类型.


主键:可以在某个字段后面添加primary key autoincrement设置成主键,这几个英文单词的意思是,设置为主键,并且主键自动递增

举例:

 
 
  1. create table if not exists t_myCode (id integer primary key autoincrement,name text,age text);


删表:

格式:

        drop table 表名;

        drop table if exists 表名;

举例:

 
 
  1. drop table if exists t_myCode;



插入数据:

格式:

        insert into 表名(字段1,字段2,...) values(字段1的值,字段2的值,...);

举例:

        //字符串内容有单引号括,不要用双引号

 
 
  1. insert into t_myCode(name,age) values('nie',22);


更新数据:

格式

        update 表名 set 字段1 = 字段1的值,字段2 = 字段2的值,...;

举例:

        //这里会将表中所有的name都改成yang,age改成30

 
 
  1. update t_myCode set name = 'yang',age = 30;


删除数据:

格式:

        delete frome 表名

举例:

        //这里会将t_mycode表中的所有数据都删除

 
 
  1. delete frome t_myCode

        接下来是查询语句,查询语句是用的最多的操作,对需要的数据进行条件判断,如果只想更新或者删除某些固定的记录,那就必须在DML语句后加上一些条件.

 
 
  1. //条件语句的常见格式

  2. where 字段 = 某个值 ;   // 不能用两个 =

  3. where 字段 is 某个值 ;   // is 相当于 =

  4. where 字段 != 某个值 ;

  5. where 字段 is not 某个值 ;   // is not 相当于 !=

  6. where 字段 > 某个值 ;

  7. where 字段1 = 某个值 and 字段2 > 某个值 ;  // and相当于C语言中的 &&

  8. where 字段1 = 某个值 or 字段2 = 某个值 ;  //  or 相当于C语言中的

        按照myCode表中年龄大于10,并且姓名不等于yang的记录,年龄都改为5

                update t_myCode set age = 5 where age >10 and name !='yang';

        删除t_myCode表中年龄小于等于10或者年龄大于30的记录

                delete from t_myCode where age <=10 or age >30;

        将表中名字等于yang的记录,height字段的值都改为age字段的值

                update t_myCode set height = age where name = 'yang';


DQL查询:

格式:

        select 字段1,字段2,...from 表名;

        select * from 表名;

举例:

        select name,age from t_myCode;

        select * from t_myCode;

        select * from t_myCode where age >10; 

        在这些查询语句中可以给字段和表名分别添加别名,select 字段1 别名1,字段2 别名2,...from 表名 别名;

在字段和别名之间可以加上as,不过可以省略,不写是一样的效果,select 字段 as 别名,...from 表名 as 别名;

举例:

        select name sname,age sage from t_mycode as c;

如果表名有别名,那么可以通过表的别名来引用表中的字段,类似于oc中的点语法

        select c.name,c.age from t_myCode as c;


        当然在sqlite中也有提供一些函数,比如计算某个字段的数量,平均数,等等

        比如:计算数量

        select count(字段) from 表名;

        select count(*) from 表名;

        select count(*) from 表名 where 条件;    


排序:数据库中默认是按照升序排列的 , 要进行排序,在后面添加 order] by 字段 desc

例如:select * from t_mycode order by age desc;

多个字段排序也行,是按照前面的排序,然后再按照后面的字段排序,有先后顺序

例如:select * from t_myCode order by age desc,height asc;

(desc是降序,asc是升序,默认)


分页控制:limit,用于精确查询结果的数量,比如每次只想查询5条数据.

格式:

        select * from 表名 limit 数值1,数值2;

意思是:跳过数值1前面的语句,然后取数值2条记录.

举例:

        select *from t_myCode limit 10,20;


简单约束:

建表时,可以给特定的字段设置一些约束条件,常见的约束有

not null:规定字段的值不能为null

unique:规定字段的值唯一

default:指定字段的默认值


 
 
  1. create table t_mycode(id integer primary key autoincrement,name text not null unique,age text not null defoult 1);

name字段不能为null,并且唯一

age字段不能为null,并且默认为1



主键约束:

        就是上面提到的主键就是这个,当表中就name和age两个字段,而且有些记录的name和age字段的值都一样时,那么就没法区分这些数据,造成数据库的记录不唯一,这样就不方便管理数据.这就需要一个主键约束,主键最好要建立在业务逻辑之外,就是说,业务逻辑中没有主键的字段.

        好的数据库编程规范应该保证每条记录的唯一性,为此,增加了主键约束,也就是说,每张表都必须要有一个主键,作为唯一标示符.

表中的某一个字段只要声明了primary key ,就是一个主键字段了,主键字段默认包含了not null和unique这个两个约束.如果想让主键自动增长应该加入autoincrement,声明上面主键有举例.


外键约束:

        利用外键约束可以建立表与表之间的联系,外检的一半情况是:一张表的某个字段,引用这另一张表中的主键字段

 
 
  1. creat table t_class(id integer primary key autoincrement,score text);

 
 
  1. create table t_student (id integer primary key autoincrement, name text, age integer,class_id integer, constraint fk_t_student_class_id_t_class_id foreign key (class_id) (id)) ; references t_class

t_student表中有一个叫做fk_t_student_class_id_t_class_id的外键 

这个外键的作用是用t_student表中的class_id字段引用t_class表的id字段


在iOS开发中如果需要使用sqlite数据库,需要导入libsqlite3.dylib二进制库,并且导入sqlite3.h头文件

首先声明实例变量

 
 
  1. sqlite3 *_db; // db代表着整个数据库,db是数据库实例

在使用前需要打开数据库,打开后才能创表

 
 
  1. // 0.获得沙盒中的数据库文件名

  2. NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];

  3. // 1.创建(打开)数据库(如果数据库文件不存在,会自动创建)

  4. int result = sqlite3_open(filename.UTF8String, &_db);

  5. if (result == SQLITE_OK) {

  6.    NSLog(@"成功打开数据库");

  7.    

  8.    // 2.创表

  9.    const char *sql = "create table if not exists t_student (id integer primary key autoincrement, name text, age integer);";

  10.    char *errorMesg = NULL;

  11.    int result = sqlite3_exec(_db, sql, NULL, NULL, &errorMesg);

  12.    if (result == SQLITE_OK) {

  13.        NSLog(@"成功创建t_student表");

  14.    } else {

  15.        NSLog(@"创建t_student表失败:%s", errorMesg);

  16.    }

  17. } else {

  18.    NSLog(@"打开数据库失败");

  19. }

之后可以进行插入更新删除操作

 
 
  1. - (void)insert

  2. {

  3.    for (int i = 0; i<30; i++) {

  4.        NSString *name = [NSString stringWithFormat:@"Jack-%d", arc4random()%100];

  5.        int age = arc4random()%100;

  6.        NSString *sql = [NSString stringWithFormat:@"insert into t_student (name, age) values('%@', %d);", name, age];

  7.        

  8.        char *errorMesg = NULL;

  9.        int result = sqlite3_exec(_db, sql.UTF8String, NULL, NULL, &errorMesg);

  10.        if (result == SQLITE_OK) {

  11.            NSLog(@"成功添加数据");

  12.        } else {

  13.            NSLog(@"添加数据失败:%s", errorMesg);

  14.        }

  15.    }

  16. }

更新和删除操作和这个插入一样,只是sqlite语言改变一下,但是这里要注意sql这个字符串如果是这样拼接可能会有sql注入的漏洞,应该由占位符来替代


以下是查询语句

 
 
  1. - (void)query

  2. {

  3.    // 1.定义sql语句

  4. const char *sql = "select id, name, age from t_student where name = ?;";

  5.    

  6.    // 2.定义一个stmt存放结果集

  7.    sqlite3_stmt *stmt = NULL;

  8.    

  9.    // 3.检测SQL语句的合法性

  10.    int result = sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL);

  11.    if (result == SQLITE_OK) {

  12.        NSLog(@"查询语句是合法的");

  13.        

  14.        // 设置占位符的内容

  15.        sqlite3_bind_text(stmt, 1, "jack", -1, NULL);

  16.        

  17.        // 4.执行SQL语句,从结果集中取出数据

  18.        while (sqlite3_step(stmt) == SQLITE_ROW) { // 真的查询到一行数据

  19.            // 获得这行对应的数据

  20.            

  21.            // 获得第0列的id

  22.            int sid = sqlite3_column_int(stmt, 0);

  23.            

  24.            // 获得第1列的name

  25.            const unsigned char *sname = sqlite3_column_text(stmt, 1);

  26.            

  27.            // 获得第2列的age

  28.            int sage = sqlite3_column_int(stmt, 2);

  29.            

  30.            NSLog(@"%d %s %d", sid, sname, sage);

  31.        }

  32.    } else {

  33.        NSLog(@"查询语句非合法");

  34.    }

  35. }


FMDB的使用

创表

 
 
  1. @property (nonatomic, strong) FMDatabase *db;

 
 
  1. // 0.获得沙盒中的数据库文件名

  2. NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];

  3. // 1.创建数据库实例对象

  4. self.db = [FMDatabase databaseWithPath:filename];

  5. // 2.打开数据库

  6. if ( [self.db open] ) {

  7.    NSLog(@"数据库打开成功");

  8.    

  9.    // 创表

  10.    BOOL result = [self.db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"];

  11.    

  12.    if (result) {

  13.        NSLog(@"创表成功");

  14.    } else {

  15.        NSLog(@"创表失败");

  16.    }

  17. } else {

  18.    NSLog(@"数据库打开失败");

  19. }

插入

 
 
  1. - (void)insert

  2. {

  3.    for (int i = 0; i<40; i++) {

  4.        NSString *name = [NSString stringWithFormat:@"rose-%d", arc4random() % 1000];

  5.        NSNumber *age = @(arc4random() % 100 + 1);

  6.        [self.db executeUpdate:@"insert into t_student (name, age) values (?, ?);", name, age];

  7.    }

  8. }

更新,在fmdb中除了查询方法其它所有方法都看做是更新,调用executeUpdate方法,只是sqlite语句不一样

"delete from t_student where name = ?;",@"jack",只要下面的sqlite替换成这个就是删除了

 
 
  1. - (void)update

  2. {

  3.    [self.db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  4. }

查询

 
 
  1. - (void)query

  2. {

  3.    // 1.查询数据

  4.    FMResultSet *rs = [self.db executeQuery:@"select * from t_student where age > ?;", @50];

  5.    

  6.    // 2.遍历结果集

  7.    while (rs.next) {

  8.        int ID = [rs intForColumn:@"id"];

  9.        NSString *name = [rs stringForColumn:@"name"];

  10.        int age = [rs intForColumn:@"age"];

  11.        

  12.        NSLog(@"%d %@ %d", ID, name, age);

  13.    }

  14. }


在FMDB中还有队列类,这是线程安全的

创建队列,并建表

 
 
  1. @property (nonatomic, strong) FMDatabaseQueue *queue;

 
 
  1. // 0.获得沙盒中的数据库文件名

  2. NSString *filename = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"student.sqlite"];

  3. // 1.创建数据库队列

  4. self.queue = [FMDatabaseQueue databaseQueueWithPath:filename];

  5. // 2.创表

  6. [self.queue inDatabase:^(FMDatabase *db) {

  7.    BOOL result = [db executeUpdate:@"create table if not exists t_student (id integer primary key autoincrement, name text, age integer);"];

  8.    

  9.    if (result) {

  10.        NSLog(@"创表成功");

  11.    } else {

  12.        NSLog(@"创表失败");

  13.    }

  14. }];

插入

 
 
  1. - (IBAction)insert

  2. {

  3.    [self.queue inDatabase:^(FMDatabase *db) {

  4.        for (int i = 0; i<40; i++) {

  5.            NSString *name = [NSString stringWithFormat:@"rose-%d", arc4random() % 1000];

  6.            NSNumber *age = @(arc4random() % 100 + 1);

  7.            [db executeUpdate:@"insert into t_student (name, age) values (?, ?);", name, age];

  8.        }

  9.    }];

  10. }

更新,队列中开启事务可以直接用这个block来自动实现,但如果是basedb可手动开启事务

 
 
  1. - (IBAction)update

  2. {

  3.    [self.queue inDatabase:^(FMDatabase *db) {

  4.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  5.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  6.        

  7. //        if (发现情况不对){

  8. //            // 回滚事务

  9. //            [db rollback];

  10. //            [db executeUpdate:@"rollback transaction;"];

  11. //        }

  12.        

  13.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  14.      

  15.    }];

  16. }

 
 
  1.        // 开启事务

  2.        [db executeUpdate:@"begin transaction;"];

  3.        [db beginTransaction];

  4.    

  5.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  6.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  7.        

  8.        

  9.        if (发现情况不对){

  10.            // 回滚事务

  11.            [db rollback];

  12.            [db executeUpdate:@"rollback transaction;"];

  13.        }

  14.    

  15.        

  16.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  17.        

  18.        // 提交事务

  19.        [db commit];

  20.        [db executeUpdate:@"commit transaction;"];


删除

 
 
  1. - (IBAction)delete

  2. {

  3.    [self.queue inTransaction:^(FMDatabase *db, BOOL *rollback) {

  4.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  5.        [db executeUpdate:@"update t_student set age = ? where name = ?;", @20, @"jack"];

  6.        

  7. //        if (发现情况不对){

  8. //            // 回滚事务

  9. //            *rollback = YES;

  10. //        }

  11.    }];

  12. }


查询,rs指向结果集的最前面.每调用next方法后指向下一个结果,知道下一个结果为空

 
 
  1. - (IBAction)query

  2. {

  3.    [self.queue inDatabase:^(FMDatabase *db) {

  4.        // 1.查询数据

  5.        FMResultSet *rs = [db executeQuery:@"select * from t_student where age > ?;", @50];

  6.        

  7.        // 2.遍历结果集

  8.        while (rs.next) {

  9.            int ID = [rs intForColumn:@"id"];

  10.            NSString *name = [rs stringForColumn:@"name"];

  11.            int age = [rs intForColumn:@"age"];

  12.            NSLog(@"%d %@ %d", ID, name, age);

  13.        }

  14.    }];

  15. }







1.打开数据库

int sqlite3_open(

    const char *filename,   // 数据库的文件路径

    sqlite3 **ppDb          // 数据库实例

);


2.执行任何SQL语句

int sqlite3_exec(

    sqlite3*,                                  // 一个打开的数据库实例

    const char *sql,                           // 需要执行的SQL语句

    int (*callback)(void*,int,char**,char**),  // SQL语句执行完毕后的回调

    void *,                                    // 回调函数的第1个参数

    char **errmsg                              // 错误信息

);


3.检查SQL语句的合法性(查询前的准备)

int sqlite3_prepare_v2(

    sqlite3 *db,            // 数据库实例

    const char *zSql,       // 需要检查的SQL语句

    int nByte,              // SQL语句的最大字节长度

    sqlite3_stmt **ppStmt,  // sqlite3_stmt实例,用来获得数据库数据

    const char **pzTail

);


4.查询一行数据

int sqlite3_step(sqlite3_stmt*); // 如果查询到一行数据,就会返回SQLITE_ROW


5.利用stmt获得某一字段的值(字段的下标从0开始)

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 void *sqlite3_column_blob(sqlite3_stmt*, int iCol); // 二进制文本数据

const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);  // 字符串数据






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值