热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS数据库Sqlite的SQL语句分类和常见约束关键字

本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括ifnotexists、ifexists、primarykey、autoincrement、notnull和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。

1 SQLite

数据库的SQL语句分为三种分别是:DDL DML DQL

1.1 约束关键字

常见约束关键字(不区分大小写):

if not exists 如果不存在
if exists 如果存在
primary key 主键
autoincrement 自增
not null 不能为空
default 默认值

常见数据库数据类型

integer 整型
text 文本
real 浮点型

1.2 DDL语句

DDL语句一般是用来操作数据表的,对数据表进行定义、删除和修改操作的。对应的关键字分别为:create/drop/alter

SQL语句示例

//1 创建数据表(创建学生表t_student)
create table if not exists t_student(id integer primary key autoincrement,name text not null,age integer,score real default 60)
//2 删除数据表 (删除学生表t_student)
drop table if exists s_student
//3 修改数据表
//--3.1 修改数据表的名称
alter table t_student rename to t_person
//--3.2 增加数据表字段
alter table t_stu add column telephone integer

1.3 DML语句

DML语句一般是用来操作数据表中的记录的。对数据表的记录进行增删改操作通称为DML语句

SQL语句示例

//1 插入记录
insert into t_stu(name,age,score) values('chmn',24,100)
//2 删除记录
delete from t_stu where name = 'chmn'
delete from t_stu where name is 'chmn'
//3 修改记录
update t_stu set score = 99 where name = 'chmn'

1.4 DQL语句

该语句就是对数据表字段进行查询操作,可以多表查询

//--1.2 定义数据库指针,用来指向打开的数据库
var db:COpaquePointer = nil
//--1.3 打开数据库(如果该路径不存在,则创建后打开)
if sqlite3_open(path, &db) == SQLITE_OK {
print("数据库打开成功")
}else {
print("数据库打开失败")
}
//2 创建数据表
//--2.1 获取SQL语句
let sql = "create table if not exists t_person(name text,age integer)"
//--2.2 执行sql语句
/*
参数1 打开的数据库
参数2 执行的sql语句
参数3 回调
参数4 回调的参数
参数5 错误信息
*/
if sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK {
print("数据表创建成功")
}else{
print("数据表创建失败")
}
//3 删除数据表
let deleSql = "drop table if exists t_person"
if sqlite3_exec(db, deleSql, nil, nil, nil) == SQLITE_OK {
print("删除表创建成功")
}else{
print("删除表创建失败")
}

一般将其进行封装成工具类,直接使用

封装的工具类 SqliteTool.swift

class SqliteTool: NSObject {
//设置单例
static let shareInstance = SqliteTool()
//定义数据库指针,用来指向打开的数据库
var db:COpaquePointer = nil
override init() {
super.init()
/**打开数据库*/ //1 打开数据库
let path = "/Users/apple/Pictures/myDb.sqlite"
//打开数据库(如果该路径不存在,则创建后打开)
if sqlite3_open(path, &db) == SQLITE_OK {
print("数据库打开成功")

}else {
print("数据库打开失败")
}
}
/**执行sql语句*/
func execute(sql:String) -> Bool
{
return (sqlite3_exec(db, sql, nil, nil, nil) == SQLITE_OK)
}

/**创建table*/
func createTable()
{
//获取SQL语句
let sql = "create table if not exists t_person(name text,age integer)"
//执行sql语句
/*
参数1 打开的数据库
参数2 执行的sql语句
参数3 回调
参数4 回调的参数
参数5 错误信息
*/
if execute(sql){
print("创建table成功")
}else{
print("创建table失败")
}
}
/**删除table*/
func dropTable()
{
//删除数据表
let sql = "drop table if exists t_person"
if execute(sql){
print("删除table成功")
}else{
print("删除table失败")
}

}
}

2.3 代码实现DML语句

定义Person类,有姓名和年龄两个字段,将这个这个实例对象插入到上述的数据表中,做更新等操作

Person.swift增加以下对象方法

2.3.1 插入

/**插入到数据表*/
func insertTable()
{
//1 常见sql
let sql = "insert into t_person(name,age) values('\(self.name)',\(self.age))"
//2 执行sql语句
if SqliteTool.shareInstance.execute(sql) {
print("插入成功")
}else{
print("插入失败")
}
}

2.3.2 删除

/**从数据表中删除*/
func deleteFromTable()
{
let sql = "delete from t_person where name = '\(self.name)'" if SqliteTool.shareInstance.execute(sql){
print("删除成功")
}else{
print("删除失败")
}}

2.3.3 修改

func updateTable()
{
let sql = "update t_person set name = '\(self.name)',age = \(self.age)"
if SqliteTool.shareInstance.execute(sql){
print("更新成功")
}else{
print("更新失败")
}
}

2.3.4 绑定插入

  • 到这里,我们所使用的插入都是直接使用的sqlite_exec方法实现的。其实,iOS提供了预处理语句来绑定插入。而且,上面的sqlite_exec其实是对绑定插入的一次封装。
  • 绑定插入,其实就是首先提供预处理语句,然后再绑定预处理语句中的参数,实现插入
  • 一般地,绑定插入比直接使用sqlite_exec的效率高,因为sqlite_exec也是对绑定插入进行的封装

绑定插入的步骤

1、 创建sql语句,参数用问号?表示

2 、根据sql创建预处理语句 sqlite3_prepare_v2

3、 分别绑定参数 sqlite3_bind_text sqlite3_bind_int sqlite3_bind_Double

4、 执行预处理语句 sqlite3_step

5、 重置预处理语句 sqlite3_reset

6、 释放预处理语句 sqlite3_finalize


一般用法:如果在大量数据插入中,1/2/7步只需要执行一次,中间循环执行3~6步骤,这样可以提高效率

/**绑定插入*/
func bindInsert()
{
//1 创建预处理语句--可变参数用 ?表示,固定写法
let db = SqliteTool.shareInstance.db //打开的数据库
let zSql = "insert into t_person(name,age,score) values(?,?,?)" //sql语句
var ppStmt : COpaquePointer = nil //生成的处理语句-传入地址
//-参数4 ,取出字符串zSql的长度。-1表示自动计算
//-参数5 ,表示zSql除去参数3指定的长度后剩下的语句
if sqlite3_prepare_v2(db, zSql, -1, &ppStmt, nil) != SQLITE_OK {
print("预处理失败")
}
//2 绑定整型
//-参数1,表示上面创建好的预编译语句
//-参数2,表示绑定zSql中的哪个问号表示的参数,是索引。从1开始的
//-参数3,对应参数2的值
sqlite3_bind_int(ppStmt, 2, 24) //绑定age = 24 //3 绑定浮点型
sqlite3_bind_double(ppStmt, 3, 89.7) //绑定score = 89.7 //4 绑定文本类型
//-参数1 预处理语句
//-参数2 绑定的索引
//-参数3 需要绑定的值
//-参数4 从参数3中取出多长的数据进行绑定,-1表示自动计算
//-参数5 对参数值的处理方法,有以下两种
//-------SQLITE_STATIC :认为参数是一个常量,不会被释放,不会做任何的引用 宏 0
//-------SQLITE_TRANSIENT:会对参数进行引用 它是一个宏,-1
//-------进行按位转换,可以进入头文件查看,因为swift是不能有宏的,所以要自己转换
//-------将该宏表示的-1转换成sqlite3_destructor_type类型
let SQLITE_TRANSIENT = unsafeBitCast(-1, sqlite3_destructor_type.self)
sqlite3_bind_text(ppStmt, 1, "zhangsan", -1, SQLITE_TRANSIENT) //5 执行预处理语句
if sqlite3_step(ppStmt) == SQLITE_DONE {
print("执行成功")
}else {
print("执行失败")
}
//6 重置预处理语句-重置绑定
sqlite3_reset(ppStmt) //7 释放预处理语句
sqlite3_finalize(ppStmt)
}

2.3.5绑定插入(sqlite3_step)与插入(sqlite3_exec)的效率分析

测试方法:

当前执行时刻的获取方法 CFAbsoluteTimeGetCurrent()

1 使用绑定插入循环插入10000条数据,计算插入开始和完成的时间差

测试中,循环执行上面代码的3~6步,计算时间差

2 创建插入sql,使用sqlite3_exec插入,计算时间差

3 比较两个效率

结论:

效率差别有,但不是很大。整体插入大数据都会出现时间很长的情况,亟待优化

2.3.6 大数据插入的优化

问题的原因

不管是绑定预处理执行(sqlite3_step)还是直接sql执行(sqlite3_exec),当进行大数据操作的时候,都会出现耗时很长的情况。其原因:

1 sqlite3_exec是对sqlite3_step的封装,效率会低

2 sqlite3_step绑定时,虽然创建预处理语句,释放预处理语句都只执行一次。但是,在每次执行数据库操作前,都会首先开启一个“事务”,执行完一个操作后,提交一个“事务”。循环执行大量操作时,开启事务和提交事务的过程会耗时非常大,这才是根本原因

优化方法

1 使用绑定操作sqlite3_step

2 创建预处理语句和释放预处理语句都保证只执行一次

3 手动开启事务和提交事务

代码

在绑定参数之前,手动开启事务

let sql = "begin transaction"
sqlite3_exec(sql)

在结束插入,释放预处理语句之前,手动提交事务

let sql = "commit transaction"
sqlite3_exec(sql)

2.4 代码实现DQL语句

查询操作也分为两种,一种是直接使用sqlite3_exec执行sql语句查询;另一种是使用sqlite3_step执行预处理语句查询

2.4.1 sqlite3_exec直接执行sql查询

此时就需要使用到了sqlite3_exec的回调方法了,每查询到一行就调用回调方法,直到回调方法返回1或者直到查询完成,回调方法中的参数分别为

  • 参数1 无意义,由slite3_exec的参数4传入的,类似于上下文之类的
  • 参数2 查询到当前行的列数(字段的个数)
  • 参数3 查询到当前行的字段值的数组 — sqlite3_exec方法查询到所有字段值都当做文本类型
  • 参数4 查询到当前行的字段名称数组
  • 返回值 表示是否终止查询,我们自行控制。如果返回0,表示一直查询直到完全查出;如果返回1,表示查询终止,不再查询了

举例:

func queryAll()
{
//1 创建sql语句
let sql = "select * from t_person"
let db = SqliteTool.shareInstance.db //打开的数据库
//2 执行查询语句 -- 每查询到一行数据就会调用这个回调方法
sqlite3_exec(db, sql, { (firstValue, columnCount, columnValues, columnNames) -> Int32 in

// 遍历当前行的所有字段(列)
let count = Int(columnCount)
for i in 0..
// 获取当前的列名(字段名)
//---类型UnsafeMutablePointer相当于UnsafeMutablePointer ,也相当于char * 指向字符串
let columnName = columnNames[i]
//---转换成字符串
let columnNameStr = String(CString: columnName, encoding: NSUTF8StringEncoding)

// 获取当前字段的值
let columnValue = columnValues[i]
let columnValues = String(CString: columnValue, encoding: NSUTF8StringEncoding)

print(columnNameStr,columnValues)

}
return 0 //如果返回0,就表示一直查询,直到查询结束;如果返回1,表示到当前位置终止查询

}, nil, nil)
}

2.4.2 sqlite3_step执行预处理语句查询

主要步骤与绑定插入的步骤一样,首先要创建预处理语句,再次绑定语句,然后执行语句,其次重置语句,最后释放语句。

执行的结果集需要我循环获取,它拿到的结果集是不仅仅是文本型,可以根据数据库存储的实际类型进行解析

举例:

func prepareQuaryAll()
{ let sql = "select * from t_person"
let db = SqliteTool.shareInstance.db
var stmt : COpaquePointer = nil
// 创建预处理语句
if sqlite3_prepare_v2(db, sql, -1, &stmt, nil) != SQLITE_OK {
print("预处理失败")
return
} // 绑定 - 如果没有需要绑定的参数,可以省略 // 执行预处理语句 -- 每执行一次就是一行数据,每行记录(结果集)都放在stmt中
while sqlite3_step(stmt) == SQLITE_ROW {

// 获取当前行有多少列(字段)
let columnCount = sqlite3_column_count(stmt)

// 循环取出每个字段
for i in 0.. // 获取当前字段的字段名称(当前索引,第几个字段)
let columnName = sqlite3_column_name(stmt, i)
let columnNameStr = String(CString: columnName, encoding: NSUTF8StringEncoding)


// 获取当前字段的类型
let type = sqlite3_column_type(stmt, i)

// 根据类型取出对应类型的值
if type == SQLITE_TEXT { //如果是文本类型
let value = sqlite3_column_text(stmt, i)
let valueInt8 = UnsafePointer(value)
let valueStr = String(CString: valueInt8, encoding: NSUTF8StringEncoding)
print(columnNameStr,valueStr)
}else if type == SQLITE_INTEGER{ //如果是整型
let value = sqlite3_column_int(stmt, i)
print(columnNameStr,value)
}else if type == SQLITE_FLOAT { //如果是浮点型
let value = sqlite3_column_double(stmt, i)
print(columnNameStr,value)
}
}
}}

3 事务Transaction(批量操作的优化)

  • 事务是并发控制的单位,是用户定义的操作序列。事务里面的操作,要么都做,要么都不做。
  • 事务是一个不可分割的工作单位。通过事务,可以把逻辑相关的一些操作绑定在一起
  • 事务通常以begin transaction开始的;以commit transaction或者rollback transaction结束的
  • 当大量的操作执行的时候,默认情况下,每个操作都会开启一个事务(成功之后提交事务,失败后回滚事务),频繁的开启事务操作会耗时很大
  • 所有,大量操作执行的时候,我们将这些操作放在一个事务中,整体只开启一个事务,成功之后提交事务,失败之后回滚事务,提高效率。

举例:把插入的语句放到一个事务transaction中执行

func insertOperation()
{
//创建插入sql
let sql1 = "insert into t_stu(name,age) values('chenhua',23)" //该数据库存在
let sql2 = "insert into t_stu2(name,age) values('lisi',24)" //该数据库不存在 ///开启事务
SqliteTool.shareInstance.execute("begin transaction") //执行操作
let result1 = SqliteTool.shareInstance.execute(sql1)
let result2 = SqliteTool.shareInstance.execute(sql2) //判断结果,处理事务
if result1 && result2 {
print("提交")
SqliteTool.shareInstance.execute("commit transaction")
}else{
print("回滚")
SqliteTool.shareInstance.execute("rollback transaction")
}

4 FMDB框架的使用

是一个面向对象的SQLite框架,对sqlite进行了封装。所以在导入框架后,需要添加框架依赖 sqlite3.0.tdb

4.1 创建数据库并打开

let path = "/Users/apple/Desktop/haha/fmdb.sqlite"
// 打开数据库
let db = FMDatabase(path: path)
db.open()

4.2 创建数据表

// 创建数据表
let tableSql = "create table if not exists t_student(name text,age integer,score real)"
if db.executeStatements(tableSql) {
print("创建数据表成功")
}else {
print("创建数据表失败")
}

4.3 更新数据(增删改使用executeUpdate方法)

// 更新数据(增删改都是用executeUpdate)
let insertSql = "insert into t_student(name,age,score) values('jim',23,22)"
if db.executeUpdate(insertSql, withArgumentsInArray: nil) {
print("插入数据成功")
}else{
print("插入数据失败")
}

4.4 查询数据(使用executeQuery方法)

// 查询数据 -- 查询的结果都放在结果集中
let querySql = "select * from t_student"
let resultSet = db.executeQuery(querySql, withArgumentsInArray: nil)
//---循环取出里面的记录
while resultSet.next() {
// 获取当前行的字段数(列数)
let columnCount = resultSet.columnCount()
// 获取指定字段的值
let name = resultSet.stringForColumn("name")
let age = resultSet.intForColumn("age")
let score = resultSet.doubleForColumn("score")
print(name,age,score)
}

4.5 执行事务

// 开启事务
db.beginTransaction() // 执行操作
let insertSql1 = "insert into t_student(name,age,score) values('jim',23,22)"
let insertSql2 = "insert into t_student2(name,age,score) values('jim',23,22)"
let result1 = db.executeUpdate(insertSql1, withArgumentsInArray: nil)
let result2 = db.executeUpdate(insertSql2, withArgumentsInArray: nil) // 提交或者回滚事务
if result1 && result2
{
db.commit()
}else{
db.rollback()
}

4.6 预处理语句

普通状态下一样,不错需要使用标准语法 用问号?代表参数

4.7 线程安全的操作数据库

不能直接使用FMDataBase开启db,然后执行操作。需要使用FMDatabaseQueue类,他在回调block中提供了db

// 创建数据库队列
let dbQueue = FMDatabaseQueue(path: path) // 在数据库中操作
dbQueue.inDatabase { (db:FMDatabase!) -> Void in

//使用db执行响应的增删改查操作
db.executeUpdate(sql,nil)

}

推荐阅读
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
author-avatar
手机用户2502856985
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有