本文介绍SQLite的使用,首先简单了解一下什么是数据库
SQLite
?SQLite
是一款轻型的嵌入式数据库(用C语言写的开源库),实现了一个自包含的SQL关系型数据库引擎
,可存储并操作大量数据,作为关系型数据库我们可以在一个数据库中建立多张相关联的表来解决大量数据重复的问题,而且SQLite
库也针对移动设备上的使用进行了优化。
SQLite
的接口使用C语言写的,而且 Objective-C是C的超集,所以可以直接在工程中使用SQLite
SQLite
占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了,而且处理速度比Mysql、PostgreSQL这两款著名的数据库都还快
Build Phrases
Link Binary With Libraries
后,点击+
号,子弹窗输入sqlite
sqlite库
即可#import <sqlite3.h>
@interface ViewController () {
sqlite3* _sqlite;
}
@end
数据定义语句(DDL:Data Definition Language)
CREATE TABLE
、DROP TABLE
或ALTER TABLE
)等操作数据操作语句(DML:Data Manipulation Language)
INSERT
、DELETE
、UPDATE
、SELECT
等操作数据查询语句(DQL:Data Query Language)
SELECT
子句、FROM
子句、WHERE
子句组成的查询块order by
、group by
和having
等数据控制语句(DCL:Data Control Language)
COMMIT
提交、ROLLBACK
回滚、GRANT
、REVOKE
等存储类 | 描述 |
---|---|
NULL | 空值 |
INTEGER | 带符号的整型值 |
REAL | 浮点值 |
TEXT | 文本字符串,使用数据库编码(UTF-8、UTF-16BE或UTF-16LE)存储 |
BLOB | 二进制数据(比如文件、模型),完全根据它的输入存储 |
实际上SQLite字段是无类型的,就算声明为INTEGER类型,仍能存储字符串文本(主键除外),建表时声明任何类型或不声明类型都可以(比如CREATE TABLE TB_One(name, age)
)
为了保持良好的编程规范,方便程序员之间的交流,编写建表语句的时候最好加上每个字段的具体类型,也是约定俗成的规则
实际上是打开指定数据库,有数据库文件就去打开它,没有就自动创建数据库
1. 获取数据库文件路径
//SQLite文件名称
static NSString* const dataBaseName = @"MyFirstDB.sqlite";
//将数据库存到沙盒中的Documents路径
//获取Documents路径
NSString* sandBoxPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
//拼接得到数据库文件地址
NSString* filePath = [sandBoxPath stringByAppendingPathComponent: dataBaseName];
SQLite文件名称自己随便起,后缀最好是.db
或.sqilte
,用来表示这是一个数据库
2. 打开指定数据库
NSInteger status = sqlite3_open(filePath.UTF8String, &_sqlite);
if (status == SQLITE_OK) {
NSLog(@"打开数据库成功");
} else {
NSLog(@"打开数据库失败");
}
sqlite3_open(const char *filename, sqlite3 **ppDb);
方法用于打开数据库连接sqlite3*
类型的引用(声明),如果要进行增删改查,就得操作_sqlite
这个实例。status
接收这个返回值filePath
是OC类型的字符串,但sqlite3_open
要求传递的是C类型的字符串,使用UTF8String
将OC风格的字符串转成C风格的字符串枚举值 SQLITE_OK
代表成功的状态:
当我们第一次打开数据库后,数据库里面什么都没有,我们需要创建一张数据库表来存放数据
1. 创建表基本格式
示例:CREATE TABLE t_Student(ID INTEGER, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL);
NAME和AGE后面的NOT NULL
约束表示在表中创建记录时这个字段不能为NULL,常见的约束还有DEFAULT
、UNIQUE
和PRIMARY KEY
约束,更多SQLite约束的总结详见这篇文章:SQLite约束
NSInteger state = sqlite3_open(filePath.UTF8String, &_sqlite);
if (state == SQLITE_OK) {
NSLog(@"打开数据库成功");
//创建表SQL语句
NSString* SQLString = @"CREATE TABLE IF NOT EXISTS t_Student(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL)";
//保存错误信息
char* errorMsg = NULL;
NSInteger result = sqlite3_exec(_sqlite, SQLString.UTF8String, NULL, NULL, &errorMsg);
if (result == SQLITE_OK) {
NSLog(@"创表成功");
} else {
NSLog(@"创表失败 -> errorMessage: %s", errorMsg);
}
} else {
NSLog(@"打开数据库失败");
}
sqlite3_exec(sqlite3 *, const char *sql, int (*callback)(void *, int, char **, char **), void *, char **errmsg);
方法用于执行除了查询语句以外的其他语句sqlite3*
类型的引用_sqlite
,第二个参数是要执行的SQL语句,第三个参数为回调参数,是一个指向函数的指针,此函数将在SQL语句执行完成后执行,如果把callback前的*
改成^
就是一个代码块,第四个参数为传递给回调函数的指针参数(可写为NULL),第五个参数为报错信息,用以代码调试sqlite3_open
一致的枚举类型返回值,因为我们可以访问到报错信息errorMsg
,所以可以不用变量result
保存来获取创建表的成功与否,如下://直接用C语言风格字符串也可
const char* SQLString = "CREATE TABLE IF NOT EXISTS t_Student(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL)";
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
NSLog(@"创表成功");
} else {
NSLog(@"创表失败 -> errorMessage: %s 当前文件路径:%s 当前行数:%d", errorMsg, __FILE__, __LINE__);
}
__FILE__
、__LINE__
为标准C语言中的预定义宏,前者用以指示本行语句所在源文件的文件,后者用以指示本行语句所在源文件中的位置信息
执行后,创建表成功,打开数据库文件查看:
先在终端输入sqlite3 数据库文件路径
,输入.tables
查看有哪些数据库表,.quit
退出数据库文件可以看到不仅有里面创建的
t_Student
表Table,还有我之后创建的t_Fellow
表
电脑一般不会预装相关查看sqlite
数据库的软件,这里通过Mac终端查看Sqlite,参考Mac通过终端查看Sqlite
既然已经成功创建了表,现将SQL语句中的IF NOT EXISTS
删去,执行创表失败相关语句:错误信息也会提示该表已经创建!
2. 有关主键
良好的数据库编程规范应该要保证每条记录的唯一性,为此增加了主键约束,每张表必须有一个主键,用来表示记录的唯一性,创表时用PRIMARY KEY
声明一个主键
示例:CREATE TABLE t_Student(ID INTEGER PRIMARY KEY, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL);
只要声明为PRIMARY KEY
,就说明是一个主键字段,主键字段默认包含了NOT NULL
和UNIQUE
两个约束
主键的设计原则:
如果想要让主键自动增长(必须是INTEGER类型),应该增加AUTOINCREMENT
,示例:CREATE TABLE t_Student (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL);
3. 删除表基本格式
仍使用sqlite3_exec
执行:
const char* SQLString = "DROP TABLE t_Fellow";
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
NSLog(@"删表成功");
} else {
NSLog(@"删表失败 -> errorMessage: %s 当前文件路径:%s 当前行数:%d", errorMsg, __FILE__, __LINE__);
}
示例:INSERT INTO t_Student (NAME, AGE) VALUES ('Jacky', 20);
数据库中的字符串内容应该用单引号``
括起来
for (int i = 0; i < 7; ++i) {
NSString* name = [NSString stringWithFormat: @"Jacky-%d号", i + 1];
NSString* SQLString = [NSString stringWithFormat: @"INSERT INTO t_Student (NAME, AGE) VALUES ('%@', %d)", name, i + 19];
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString.UTF8String, NULL, NULL, &errorMsg);
if (!errorMsg) {
NSLog(@"插入数据成功");
} else {
NSLog(@"插入数据失败 -> errorMessage: %s", errorMsg);
}
}
查看打印日志:
终端查看数据库表的内容:
const char* SQLString = "DELETE FROM t_Student WHERE NAME = 'Jacky-4号'";
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
NSLog(@"删除数据成功");
} else {
NSLog(@"删表数据失败 -> errorMessage: %s", errorMsg);
}
const char* SQLString = "UPDATE t_Student SET NAME = 'Jackson-2号' WHERE AGE = 20";
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
NSLog(@"更新数据成功");
} else {
NSLog(@"更新数据失败 -> errorMessage: %s", errorMsg);
}
SELECT查询操作也可以使用sqlite3_exec
执行,但通常使用sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);
函数,该函数将SQL语句编译成SQLite虚拟机指令,并将编译后的SQLite3语句对象存储在ppStmt
指向的指针中,以备执行。如果pzTail
不是NULL,则该指针将指向SQL语句中未编译部分的起始位置
sqlite3_prepare_v2
函数是用来做SQL查询之前的准备工作的,其返回一个枚举值作为准备工作的结果sqlite3*
类型数据库的引用,第二个参数是要执行的SQL语句,第三个参数是SQL语句的长度(如果设置为-1,则代表系统会自动计算SQL语句的长度),第四个参数用于存储查询结果,第五个参数是指向无法使用的部分的指针,一般不会用到,写成NULL即可//查询age不等于19的数据
const char* SQLString = "SELECT ID, NAME, AGE FROM t_Student WHERE AGE != 19";
//用stmt取出查询结果
sqlite3_stmt* stmt = NULL;
NSInteger state = sqlite3_prepare_v2(_sqlite, SQLString, -1, &stmt, NULL);
if (state == SQLITE_OK) {
NSLog(@"查询语句没有问题 读取数据成功");
//每调用一次sqlite3_step函数,stmt就会指向下一条数据
while (sqlite3_step(stmt) == SQLITE_ROW) { //找到一条记录
//读取数据
//取出第0列字段的值(int类型)
int ID = sqlite3_column_int(stmt, 0);
//取出第1列字段的值(text类型)
const unsigned char* name = sqlite3_column_text(stmt, 1);
//取出第2列字段的值(int类型)
int age = sqlite3_column_int(stmt, 2);
printf("%d %s %d\n", ID, name, age);
}
} else {
NSLog(@"查询语句有问题 读取数据失败");
}
sqlite3_step(stmt)
函数将会执行查询并且将查询到的当前记录存入到stmt
中stmt
中,第二次执行会将表中的第二条数据存到stmt
中while (sqlite3_step(stmt) == SQLITE_ROW)
将会遍历表中的数据,而SQLITE_ROW
枚举值判断的是有读取到数据的行的情况执行情况:
如果只想给内容更新或者删除某些指定的数据,就需在DML语句上加上条件语句
以下为条件语句常见格式:
=
is
相当于=
is not
相当于!=
and
相当于C语言中的&&
or
相当于C语言中的||
示例:将t_Student表中年龄小于等于25或者年龄大于21的数据,NAME都改为JackyUPDATE t_Student SET NAME = 'Jackson' WHERE AGE > 21 and AGE <= 25;
给字段名、表名起别名,基本格式如下:
示例:
给字段起别名SELECT NAME USER, AGE AS OLD, SCORE POINT FROM t_Student;
给表t_Student起个别名s, 利用s来引用表中的字段SELECT s.NAME, s.AGE FROM t_Student s;
基本格式:
示例:SELECT COUNT (*) FROM t_Student WHERE AGE > 21;
根据某个字段的值进行排序搜索,基本格式如下:
SELECT * FROM t_Student ORDER BY AGE
SELECT * FROM t_Student ORDER BY AGE ASC
;也可以变为降序(从大到小)SELECT * FROM t_Student ORDER BY AGE DESC
SELECT * FROM t_Student ORDER BY AGE ASC, ID DESC;
使用LIMIT可以精确地控制查询结果的数量,基本格式如下:
示例:比如现在有6条数据,SELECT * FROM t_Student LIMIT 2, 4;
就表示跳过最前面2条数据,然后取4条数据
LIMIT经常用来做分页查询,比如每页固定显示7条数据,那么应该这样取数据:
第1页:LIMIT 0, 7 第2页:LIMIT 7, 7 第3页:LIMIT 14, 7 … 第n页:LIMIT (n - 1)*7, 7
本文总结了在iOS开发中用OC语言如何去使用SQLite数据库,介绍了SQLite相关函数,以及SQLite的基本语句
iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦,于是就出现了一系列将SQLite封装的库:CoreData(Apple官方)、FMDB(第三方),后续将会对这两个基于SQLite编写的库进行学习!