swift微博第25天(SQLite)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: swift微博第25天(SQLite)

一、数据库的简单介绍



  • 数据库(Database)是按照数据结构来组织、存储和管理数据的仓库
  • 数据库可以分为2大种类:关系型数据库(主流)对象型数据库
  • 常用关系型数据库


  • PC 端:Oracle(收费)、MySQL(国内免费)、SQL Server 、Access、DB2、Sybase
  • 嵌入式\移动客户端:SQLite


二、SQLite的介绍



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


三、SQL和SQL语句的介绍以及SQL语句的特点



  • SQL(structured query language):结构化查询语言,它还是一种对关系型数据库中的数据进行定义和操作的语言,而且它的语言简洁,语法简单,好学好用。
  • SQL语句:使用SQL语言编写出来的句子\代码,就是SQL语句,在程序运行的过程中,要想操作(增删改查)数据库中的数据,必须使用SQL语句
  • SQL语句的特点
  • 不区分大小写(比如数据库的person和PerSon是一样的)
  • 每条语句必须以分号结尾(;)
  • SQL中的关键字有很多
    select、insert、update、delete、from、create、where、desc、order等等
  • 数据库不可以使用关键字来命名表、字段
  • SQLlite数据存储类型:integer(整型值),real(浮点值),text(文本字符串),blob(二进制数据,例如文件)


四、SQL语句的种类(DDL,DML,DQL)


下面的学习会涉及到一个数据库工具Navicat,请到QQ群:584599353的文件库常用软件下载,找群主要注册码


  • 1、数据定义语句(DDL: Data Definition Language),包括create和drop等操作,在数据库中建表或删除表(create table和drop table)


  • 具体的使用:
  • 1.1、创建表的语句
  • IF NOT EXISTS  判断创建的表是否存在,只有不存在才会创建
  • PRIMARY KEY 代表该字典是主键
  • AUTOINCERMENT 代表字段自动增长


CREATE TABLE IF NOT EXISTS T_Person2(
        id INTEGER PRIMARY KEY AUTOINCERMENT,
        name TEXT,
        age INTEGER
);
  • 1.2、删除表( IF EXISTS 判断是否存在,只有存在才会删除)


DROP TABLE IF EXISTS T_Person2;
  • 2、数据操作语句(DML: Data Manipulation Language),包括insert、update、delete等操作,上面的三种操作分别用于添加、修改,删除表中的数据
  • 2.1、添加(插入数据:insert)(注意:前后两个括号是对应的,数据库中的字符串要加单引号)


INSERT INTO T_person (name,age) VALUES ('wc',26);
  • 2.2、更新数据(update)(下面的代码会把数据库中的所有名字和年龄都改了)


UPDATE T_person SET name = 'jk';更改了所有人名字
 UPDATE T_person SET name = '王冲' WHERE age = 30; 更改了年龄等于30人的名字
  • 2.3、删除数据(下面的是不对的,会删除整个表的)


DELETE FROM T_person; 删除表中所有的数据了
 DELETE FROM T_person WHERE age = 28; 删除了表中年龄等于28的人
  • 2.4、条件语句的常见格式


WHERE 字段 = 某个值; 不可以用两个=
 WHERE 字段 is 某个值; is 相当于 =
 WHERE 字段 != 某个值
 WHERE 字段  is not 某个值  is not 相当于 !=
 WHERE 字段 > 某个值;
 WHERE 字段1 = 某个值 and 字段2 > 某个值  // and相当于oc里面的&& 
 WHERE 字段1 = 某个值 or 字段2 = 某个值  // and相当于oc里面的||
  • 3、数据查询语句(DQL: Data Query Langue),可以查询获得表中的数据,关键字select是DQL(也是左右SQL)用的最多的操作,其他DQL常用的关键字有where,order by,group by 和having
  • 3.1、查询整个表


SELECT * FROM T_Person2;   //  T_Person2是表名
  • 3.2、查询符合条件的表中数据(年龄大于13的人)


SELECT name,age FROM T_Person2 WHERE age > 13;
  • 3.3、给字段起别名


SELECT name as nameJ,age as ageK FROM T_Person2;


image.png

  • 3.4、给表起别名 (同时查询不同的表,里面包含相同的字段的时候)


SELECT table1.name,table1.age,table2.name,table2.age FROM T_Person2 as table1,T_person as table2;
  • 3.5、计算记录的数量
  • SELECT count(*或者字段) FROM T_Person2(表名)   : 查询表中某一个字段有多少条


SELECT count(*) FROM T_Person2
  • 加约束条件的查询 (年龄大于20的条数)


SELECT count(*) FROM T_Person2 WHERE age > 20
  • 3.6、查询的结果用
  • 单个字段排序(默认是升序ASC,降序是DESC


SELECT * FROM T_Person2 ORDER BY age DESC
  • 多个字段排序 (年龄按照降序排序,遇到年龄相同的,再按降序排序)


SELECT * FROM T_Person2 ORDER BY age DESC , id DESC;
  • 3.6、LIMIT分页查询
  • LIMIT数值1,数值2
  • 数值1:代表跳过几条
  • 数值2:代表取几条
  • 如果只取一个数值代表取前几条


取前3条
SELECT * FROM T_Person2 LIMIT 3;
跳过前3条取后面的4条
SELECT * FROM T_Person2 LIMIT 3,4;
  • 如果按照上面的分页,下面可以用一个公式概括


10 代表一页10条数据 n代表第几页
SELECT * FROM T_Person2 LIMIT 10*(n-1),10;
  • 3.7、对建表的字段的简单约束(如下面的方式建立一个表)
  • NOT NULL : 规定字段的值不能为null
  • UNIQUE: 规定字段的值不能一样
  • DEFAULT: 指定的字段设置默认值


CREATE TABLE IF NOT EXISTS T_Person5(
     id INTEGER PRIMARY KEY AUTOINCREMENT,
     name TEXT NOT NULL,
     age INTEGER DEFAULT 20
 );


五、SQLite在Xcode里面的运用



  • 5.1、下面会按照创建数据库、建表、插入数据、更新数据、删除数据、多条数据的插入、多条数据插入时间的优化(事件的添加)、预插入数据和事件的结合。
  • 5.2、创建一个单例类JKSQLiteManger


import UIKit
    class JKSQLiteManger: NSObject {
       private static let manger: JKSQLiteManger = JKSQLiteManger()
       // 单粒
       class func shareManger() -> JKSQLiteManger {
          return manger;
       }
    }
  • 5.3、创建数据库建表(在使用之前打开,一般可以放到AppDelegate里面),下面的 \n是为了打印换行方便看, +是为了把上下两句话连成一句话,数据的名字要定义为 JK.sqlite的格式,下面T_Person8是表名,name,age:字段名,id:主键名


private var db: OpaquePointer? = nil
// MARK: 打开数据库
func openDB(SQLiteName: String) {
  // 0.拿到数据库的路径
  let path = String.cacheDir() + "/\(SQLiteName)"
  print(path)
  let cPath = path.cString(using: String.Encoding.utf8)!
  // 1.打开数据库
  /**
     1.1、cPath: 需要打开的数据库文件的路径,这里的路径是C语言字符串的路径
     1.2、打开之后的数据库对象(指针),以后所有的数据库操作,都必须拿到这个指针才能进行相关的操作
   */
  // open方法的特点:如果指定路径的数据库文件已经存在,就会直接打开,否则就会创建一个新的
  if sqlite3_open(cPath, &db) != SQLITE_OK{
      print("打开数据库失败")
      return
  }
  // 2.创建表
  if creatTable(){
      print("创建表成功")
  }else{
      print("创建表失败")
  }
}
//MARK: 创建表
func creatTable() -> Bool {
  // 1、编写SQL语句
  /**
      建议:在开发中编写SQL语句,如果语句过长,不要写在一行
      技巧:在做数据库开发时候,如果遇到错误,可以先将SQL打印出来,拷贝到PC工具中验证之后再进行调试
   */
  let sql = "\n CREATE TABLE IF NOT EXISTS T_Person8( \n" +
  "id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
  "name TEXT, \n" +
  "age INTEGER \n" +
  "); \n"
  print(sql)
  // 2、执行SQL语句
  // 在SQLite3中,除了查询以外(创建/删除/新增/更新)都使用同一个函数
  return execSQL(sql: sql)
}
  • 5.4、插入更新删除 一条数据


/** 插入一条数据*/
let sql = "INSERT INTO T_Person8 (name,age) VALUES ('\(name!)',\(age));"
/** 更新一条数据*/
let sql = "UPDATE T_Person8 SET name = '\(name)' WHERE age = \(self.age);"
/** 删除一条数据*/
let sql = "DELETE FROM T_Person8 WHERE age = \(self.age);"
 // MARK:执行除查询以外的SQL语句
 /**
    - Parameter sql: 要执行的SQL语句
    - Returns: 是否执行成功 true:执行成功 false:执行失败
  */
func execSQL(sql: String) -> Bool {
  // 1.将Swift的字符串转换为C语言的字符串
  let cSQL = sql.cString(using: String.Encoding.utf8)!
  // 在SQLite3中,除了查询以外(创建/删除/新增/更新)都使用同一个函数
  /**
     1、已经打开的数据库对象
     2、需要执行的SQLite语句,C语言字符串
     3、执行SQLite语句后面的回调,一般传nil
     4、第三个参数的第一个参数,一般传nil
     5、错误信息一般传nil
   */
   if sqlite3_exec(db, cSQL, nil, nil, nil) != SQLITE_OK{
        return false
    }
    return true
 }
  • 5.5、查询数据


let sql = "SELECT * FROM T_Person8;"
 let res = JKSQLiteManger.shareManger().execRecordSQL(sql: sql)
 // MARK:查询所有的数据
 func execRecordSQL(sql: String) ->[[String: AnyObject]] {
  // 0.将Swift字符串转换为C语言字符串
  let cSQL = sql.cString(using: String.Encoding.utf8)!
  // 1.准备数据
  // 准备: 理解为预编译SQL语句, 检测里面是否有错误等等, 它可以提供性能
  /*
   1.已经开打的数据库对象
   2.需要执行的SQL语句
   3.需要执行的SQL语句的长度, 传入-1系统自动计算
   4.预编译之后的句柄, 已经要想取出数据, 就需要这个句柄
   5. 一般传nil
   */
  var stmt: OpaquePointer? = nil
  if sqlite3_prepare_v2(db, cSQL, -1, &stmt, nil) != SQLITE_OK
  {
      print("准备失败")
  }
  // 准备成功
  var records = [[String: AnyObject]]()
  // 2.查询数据
  // sqlite3_step代表取出一条数据, 如果取到了数据就会返回SQLITE_ROW
  while sqlite3_step(stmt) == SQLITE_ROW
  {
      // 获取一条记录的数据
      let record = recordWithStmt(stmt: stmt!)
      // 将当前获取到的这一条记录添加到数组中
      records.append(record)
  }
  // 3.关闭STMT
  // 注意点: 只要用到了stmt, 一定要关闭
  sqlite3_finalize(stmt)
  // 返回查询到的数据
  return records
}
 /**
   获取一条记录的值
   :param: stmt 预编译好的SQL语句
   :returns: 字典
 */
 private func recordWithStmt(stmt: OpaquePointer) ->[String: AnyObject]
 {
  // 2.1拿到当前这条数据所有的列
  let count = sqlite3_column_count(stmt)
  //            print(count)
  // 定义字典存储查询到的数据
  var record  = [String: AnyObject]()
  for index in 0..<count
  {
      // 2.2拿到每一列的名称
      let cName = sqlite3_column_name(stmt, index)
      //let name = String(CString: cName, encoding: NSUTF8StringEncoding)!
      let name = String(cString: cName!, encoding: String.Encoding.utf8)
      //                print(name)
      // 2.3拿到每一列的类型 SQLITE_INTEGER
      let type = sqlite3_column_type(stmt, index)
      //                print("name = \(name) , type = \(type)")
      switch type
      {
      case SQLITE_INTEGER:
          // 整形
          let num = sqlite3_column_int64(stmt, index)
          record[name!] = Int(num) as AnyObject
      case SQLITE_FLOAT:
          // 浮点型
          let double = sqlite3_column_double(stmt, index)
          record[name!] = Double(double) as AnyObject
      case SQLITE3_TEXT:
          // 文本类型
          let cText = UnsafePointer(sqlite3_column_text(stmt, index))
          let text =  String.init(cString: cText!)
          record[name!] = text as AnyObject
      case SQLITE_NULL:
          // 空类型
          record[name!] = NSNull()
      default:
          // 二进制类型 SQLITE_BLOB
          // 一般情况下, 不会往数据库中存储二进制数据
          print("")
      }
  }
  return record
}
  • 5.6、大批量数据插入的优化问题(下面将使用事务异步串行的队列来快速的插入数据)


// MARK: 事务相关
 // 1.开启事务
 func beginTransaction(){
    execSQL(sql: "BEGIN TRANSACTION")
 }
 // 2.提交事务
 func commitTransaction(){
    execSQL(sql: "COMMIT TRANSACTION")
 }
 // 3.回滚
 func rollbackTransaction(){
   execSQL(sql: "ROLLBACK TRANSACTION")
 }
// MARK: 创建一个异步串行队列来执行数据的插入,防止界面卡顿
private let dbQueue = DispatchQueue(label: "com.520it.lnj")
func execQueueSQL(action:@escaping (_ manager: JKSQLiteManger)->())
{
  // 1.开启一个子线程
  dbQueue.async {
      //print(Thread.current)
      // 2.执行闭包
      action(self)
   }
}
  • 5.7、预编译优化数据库


// MARK: 预编译优化数据库
func batchExecSQL() {
 let start = CFAbsoluteTimeGetCurrent()
 let manager = JKSQLiteManger.shareManger()
 // 开启事务
 manager.beginTransaction()
 for i in 0..<10000
 {
     let sql = "INSERT INTO T_Person8" +
         "(name, age)" +
         "VALUES" +
     "(?, ?);"
     manager.batchExecSQL(sql: sql, args: "yy +\(i)", 1 + i)
 }
 // 提交事务
 manager.commitTransaction()
 print("耗时 = \(CFAbsoluteTimeGetCurrent() - start)")
}
// 自定义一个SQLITE_TRANSIENT, 覆盖系统的
private let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
// MARK: - 预编译优化数据库
func batchExecSQL(sql:String, args: CVarArg...) -> Bool
{
 // 1.将SQL语句转换为C语言
 let cSQL = sql.cString(using: String.Encoding.utf8)!
 // 2.预编译SQL语句
 var stmt: OpaquePointer? = nil
 if sqlite3_prepare_v2(db, cSQL, -1, &stmt, nil) != SQLITE_OK
 {
     print("预编译失败")
     sqlite3_finalize(stmt)
     return false
 }
 // 3.绑定数据
 var index:Int32 = 1
 for objc in args
 {
     if objc is Int
     {
         //print("通过int方法绑定数据 \(objc)")
         // 第二个参数就是SQL中('?', ?)的位置, 注意: 从1开始
         sqlite3_bind_int64(stmt, index, sqlite3_int64(objc as! Int))
     }else if objc is Double
     {
         //print("通过Double方法绑定数据 \(objc)")
         sqlite3_bind_double(stmt, index, objc as! Double)
     }else if objc is String
     {
         //                print("通过Text方法绑定数据 \(objc)")
         let text = objc as! String
         let cText = text.cString(using: String.Encoding.utf8)!
         // 第三个参数: 需要绑定的字符串, C语言
         // 第四个参数: 第三个参数的长度, 传入-1系统自动计算
         // 第五个参数: OC中直接传nil, 但是Swift传入nil会有大问题
         /*
          typedef void (*sqlite3_destructor_type)(void*);
          #define SQLITE_STATIC      ((sqlite3_destructor_type)0)
          #define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)
          第五个参数如果传入SQLITE_STATIC/nil, 那么系统不会保存需要绑定的数据, 如果需要绑定的数据提前释放了, 那么系统就随便绑定一个值
          第五个参数如果传入SQLITE_TRANSIENT, 那么系统会对需要绑定的值进行一次copy, 直到绑定成功之后再释放
          */
         sqlite3_bind_text(stmt, index, cText, -1, SQLITE_TRANSIENT)
     }
     index = index + 1
 }
 // 4.执行SQL语句
 if sqlite3_step(stmt) != SQLITE_DONE
 {
     print("执行SQL语句失败")
     return false
 }
 // 5.重置STMT
 if sqlite3_reset(stmt) != SQLITE_OK
 {
     print("重置失败")
     return false
 }
 // 6.关闭STMT
 // 注意点: 只要用到了stmt, 一定要关闭
 sqlite3_finalize(stmt)
 return true
}


最后奉上练习的demo: SwiftSQLite

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
Swift
swift微博第21天(图片选择器)
swift微博第21天(图片选择器)
475 0
swift微博第21天(图片选择器)
|
Swift
swift微博第15天(新版的判断以及跟控制器的切换)
swift微博第15天(新版的判断以及跟控制器的切换)
173 0
swift微博第15天(新版的判断以及跟控制器的切换)
|
Swift
swift微博第14天(新特性的引导图)
swift微博第14天(新特性的引导图)
188 0
swift微博第14天(新特性的引导图)
|
安全 Swift 数据安全/隐私保护
swift微博第12天(OAuth授权)
swift微博第12天(OAuth授权)
172 0
swift微博第12天(OAuth授权)
|
程序员 API Swift
swift微博第11天(三方框架的导入和手动导入的桥接以及swift单粒)
swift微博第11天(三方框架的导入和手动导入的桥接以及swift单粒)
174 0
swift微博第11天(三方框架的导入和手动导入的桥接以及swift单粒)
|
Swift
swift微博第9天(自定义微博首页的菜单)
swift微博第9天(自定义微博首页的菜单)
178 0
swift微博第9天(自定义微博首页的菜单)
|
Swift
swift微博第7天(导航条按钮的封装)
swift微博第7天(导航条按钮的封装)
139 0
swift微博第7天(导航条按钮的封装)
|
Swift
swift微博第6天(未登录界面的完善)
swift微博第6天(未登录界面的完善)
118 0
swift微博第6天(未登录界面的完善)
|
JSON Swift 数据格式
swift微博第3天(动态加载控制器)
swift微博第3天(动态加载控制器)
114 0
swift微博第3天(动态加载控制器)
|
Swift
swift微博第2天(命名空间和控制器字符串)
swift微博第2天(命名空间和控制器字符串)
148 0
swift微博第2天(命名空间和控制器字符串)

相关课程

更多