开头
- 这是分篇章,整篇请看零基础iOS开发学习日记
SQLite数据库
实际用处
- 保存一些实效性要求不高的缓存数据,例如聊天记录、朋友圈内容
- 保存用户信息
基础用法
- 基础用法围绕增删改查进行
- 数据库操作调用
sqlite3.h
文件,用的是C的函数,且由于OC的类型不像swift有Any
,也没有自动数据类型的检查,所以会有点纠结 - 在进行数据库造作之前,要初始化一个全局数据库句柄
sqlite3 *db;
,所有对数据库的操作都依赖于此句柄,类似于数据库的标记 - 对应的可视化软件是
Nacicat for SQLite
打开表
sqlite3_open(<#const char *filename#>, <#sqlite3 **ppDb#>)
参数介绍
参数
1.数据库的全路径 Int8 -> uint8 -> byte / char c语言的字符串
2.全局数据库方法‘句柄’ -> 指针
返回值 == SQLITE_OK 表示成功
如果数据库不存在,会创建数据库,如果数据库存在,会打开数据库
复制代码
//获取绝对路径 NSLibraryDirectory NSLibraryDirectory
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
path = [path stringByAppendingPathComponent:@"demo.db"];
//const在指针左侧 修饰指针所指向的变量不变
//const在指针右侧 修饰指针 地址不变
//NSString -> char
const char *cPath = [path cStringUsingEncoding:NSUTF8StringEncoding];
//打开,要进行错误判断
if (sqlite3_open(cPath, &db) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"success");
复制代码
创建表
- 创表SQL语句格式
CREATE TABLE 创建表
IF NOT EXISTS 判断表是否存在,如果存在就不执行
'T_Person' 表名
(
字段 INTEGER(整数)/ real(小数)/ TEXT (字符串)/ BOLB(二进制数据-通常不会保存在数据库中)
NOT NULL 不允许为空,主键必须有内容
PRIMARY KEY 主键
AUTOINCREMENT 自动增长
, 逗号区分字段,最后一个字段不需要
)
复制代码
- SQL执行语句
sqlite3_exec(<#sqlite3 *#>, <#const char *sql#>, <#int (*callback)(void *, int, char **, char **)#>, <#void *#>, <#char **errmsg#>)
1.全局数据库句柄
2.要执行的sql
3.执行完成sql的调用的c语言函数指针,通常传入nil
4.第三个参数的函数参数的地址,通常传入nil
5.错误信息,有其他方式获取执行情况,通常传入nil
复制代码
//创建
NSString *sql = @"CREATE TABLE IF NOT EXISTS 'local' ("
"'id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
"'name' TEXT,"
"'age' INTEGER,"
"'adult' blob,"
"'height' real"
");";
const char *cSql = [sql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
if (sqlite3_exec(db, cSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"success");
复制代码
插入
- 插入SQL语句格式
INSERT INTO 往哪个数据表里插入数据
(字段1, 字段2...)
VALUES 值
(值1, 值2....)
复制代码
NSString *insertSql = @"INSERT INTO local (id, name, age, adult, height) VALUES (2, 'liliu', 18, 0, 175.5);";
const char *cInsertSql = [insertSql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
if (sqlite3_exec(db, cInsertSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"success");
复制代码
删除
- 删除SQL语句格式
DELETE FROM 从哪个表里删
WHERE 主键 = id
复制代码
NSString *deleteSql = @"DELETE FROM local WHERE id = 2;";
const char *cDeleteSql = [deleteSql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
if (sqlite3_exec(db, cDeleteSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"success");
复制代码
修改
- 修改SQL语句格式
UPDATE 表名
SET
字段1 = 值1,字段2 = 值2 ...
WHERE 主键 = id
复制代码
NSString *updateSql = @"UPDATE local SET name = 'wangwu', age = 80 WHERE id = 2;";
const char *cUpdateSql = [updateSql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
if (sqlite3_exec(db, cUpdateSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"success");
复制代码
代替
- 注意
1.sql语句中必须包含主键
2.如果指定的主键不存在,新增记录
3.如果指定的主键存在,修改记录
条件 主键不能是自增长的,因为主键数值需要指定
复制代码
- SQL语句
INSERT OR REPLACE INTO Test (id, name, age) VALUES (2, 'wei', 99);
复制代码
NSString *replaceSql = @"INSERT OR REPLACE INTO Test (id, name, age) VALUES (2, 'wei', 99);";
const char *cReplaceSql = [replaceSql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
if (sqlite3_exec(db, cReplaceSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"success");
复制代码
查询
- 查询SQL语句格式
-- 查询所有记录,不建议写 * 通配符,直接阅读代码,无法知道查询返回的内容,可以用在测试中
-- SELECT * FROM T_Person;
-- 写明字段,便于阅读
-- SELECT id, name, height, age FROM T_Person;
-- 查询数量
SELECT count(*) FROM T_Person;
-- 指定条件统计查询
SELECT count(*) FROM T_Person WHERE height > 1.5;
-- 查询最大值 应用场景:查询经验最高的值,最近加入的人...
SELECT MAX(age) FROM T_Person;
-- 分页 LIMIT 从第几条开始(起始的条数是0),返回的记录行数
-- SELECT id, name, height, age FROM T_Person
-- LIMIT 1, 2;
-- LIMIT WHERE 组合使用,方便的作出分页功能
SELECT id, name, height, age FROM T_Person
WHERE id >= 3
LIMIT 2;
-- 排序 默认升序 ASC / 降序 DESC
-- 排序是按照指定的条件,由左至右依次排列,先排列左边的条件
SELECT id, name, height, age FROM T_Person
ORDER BY name DESC, age ASC;
-- 模糊查询
-- %可以匹配任何内容,%内容% 表示只要出现了这个内容,都会被搜索出来
-- 逻辑判断 AND OR NOT
SELECT id, name, height, age FROM T_Person
WHERE (name LIKE '%a%' AND age > 80) OR name = 'wangwu';
复制代码
- 预编译SQL函数
sqlite3_prepare_v2(<#sqlite3 *db#>, <#const char *zSql#>, <#int nByte#>, <#sqlite3_stmt **ppStmt#>, <#const char **pzTail#>)
1.全局数据库句柄
2.要执行sql的c语言字符串
3.要执行sql的以字节为单位的长度,但是如果传入-1,sql框架会自动计算
4.stmt 预编译指令,后续针对本次查询,所有的操作都基于本句柄,一定要释放;通过step函数,能够顺序获取其中的结果
5.关于stmt尾部参数的指针,通常传入nil
返回值 如果编译成功,表示能够正常执行 返回 SQLITE_OK
复制代码
NSString *querySql = @"SELECT * FROM local;";
const char *cQuerySql = [querySql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
//预编译指令,可以想成每一行
sqlite3_stmt *stmt;
//预编译
if (sqlite3_prepare_v2(db, cQuerySql, -1, &stmt, nil) != SQLITE_OK) {
NSLog(@"failed");
sqlite3_finalize(stmt);
return;
}
NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:10];
//有下一行的时候就执行
while (sqlite3_step(stmt) == SQLITE_ROW) {
//获得字段数
int cols = sqlite3_column_count(stmt);
NSMutableDictionary *rowDic = [NSMutableDictionary new];
//循环每一个字段
for (int col = 0; col < cols; col++) {
//获得字段名
const char *cName = (const char *)sqlite3_column_name(stmt, col);
//char -> NSString
NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
//获得值
const char *cValue = (const char *)sqlite3_column_text(stmt, col);
NSString *value = [NSString stringWithCString:cValue encoding:NSUTF8StringEncoding];
//赋值
rowDic[name] = value;
}
//保存一条完整的信息
[arr addObject:rowDic];
}
NSLog(@"success");
NSLog(@"%@", arr);
复制代码
小结
- 不难看出,除了查询的操作比较特殊外,增删改只需要准备好对应的语句,并且用
sqlite3_exec
调用即可 - 而SQL语句的获得,可以通过可视化软件
Nacicat for SQLite
来获得
事务
实际用处
- 处理大量数据需要打开,提高效率
- 在SQL中,如果不显式地开启事务,每一条数据库的操作指令都会开启事务,执行完毕后,提交事务;如果显式地调用,就不再开启事务
- 打开后,会对数据库进行快照,修改数据库成功则继续,失败则恢复快照,快照实质上就是先前的状态
- 事务中,如果错误要进行回滚,错误中要进行break
基础用法
- 下面以插入20000条数据为例,可以试试不用事物的插入时间
//获取当前时间
CFTimeInterval start = CACurrentMediaTime();
//开启事务
NSString *transSql = @"BEGIN TRANSACTION;";
const char* cTransSql = [transSql cStringUsingEncoding:NSUTF8StringEncoding];
if (sqlite3_exec(db, cTransSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"TRANSACTION success");
//插入大量数据
for (int i = 0; i < 20000; i++) {
NSString *insertSql = [NSString stringWithFormat:@"INSERT INTO local (name, age, adult, height) VALUES ( 'liliu-%d', 18, 0, 175.5);", i];
const char *cInsertSql = [insertSql cStringUsingEncoding:NSUTF8StringEncoding];
//执行语句
if (sqlite3_exec(db, cInsertSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
//模拟错误
// if (i == 1000) {
//错误回滚
// NSString *rollbackSql = @"ROLLBACK TRANSACTION;";
// const char* cRollbackSql = [rollbackSql cStringUsingEncoding:NSUTF8StringEncoding];
// if (sqlite3_exec(db, cRollbackSql, nil, nil, nil) != SQLITE_OK) {
// NSLog(@"failed");
// return;
// }
// NSLog(@"ROLLBACK success");
//中断
// break;
// }
}
//如果插入正确,则提交事务
NSString *commitSql = @"COMMIT TRANSACTION;";
const char* cCommitSql = [commitSql cStringUsingEncoding:NSUTF8StringEncoding];
if (sqlite3_exec(db, cCommitSql, nil, nil, nil) != SQLITE_OK) {
NSLog(@"failed");
return;
}
NSLog(@"COMMIT success");
//获取整个流程的时间 0.231517
NSLog(@"%f", CACurrentMediaTime() - start);
复制代码
性能测试
- 取绝对时间的函数
CFAbsoluteTimeGetCurrent() 核心框架的 会收到系统服务的影响,进行时间校准;在做性能测试的时候,会有误差
CACurrentMediaTime() 核心动画的,只和硬件时间有关
复制代码
总结
- iOS操作数据库,最重要的点就是SQL语句的编写,最好是要经过可视化软件测试后,在写入程序
- 在实际开发中,一行数据其实就是对应一个对象,所以往往是将数据库中的字段封装成对象的属性,方便操作
- 而数据库执行方法有很多一样的地方,往往是封装成一个单例管理类,进行操作,结构一般为
控制器
调用对象
,对象
调用数据库管理类
FMDM
- 放在第三方库去整理
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END