swift sqlite3笔记

添加libsqlite3.tbd General -> Linked Frameworks and Libraries -> 添加libsqlite3.tbd
添加桥接 Build Settings -> (搜索 Bridg)Objective-C Bridging Header -> 添加 项目名/Bridge.h
Bridge.h 添加一行如下:
#import <sqlite3.h>


    extension String{
        /**
         将当前字符串拼接到cache目录后面
         */
        func cacheDir() -> String{
            let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!  as NSString
            return path.stringByAppendingPathComponent((self as NSString).lastPathComponent)
        }
        /**
         将当前字符串拼接到doc目录后面
         */
        func docDir() -> String
        {
            let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true).last!  as NSString
            return path.stringByAppendingPathComponent((self as NSString).lastPathComponent)
        }
        /**
         将当前字符串拼接到tmp目录后面
         */
        func tmpDir() -> String
        {
            let path = NSTemporaryDirectory() as NSString
            return path.stringByAppendingPathComponent((self as NSString).lastPathComponent)
        }
    }

    打开数据库 :SQLiteManager.shareManager().openDB("sql.sqlite")
    关闭数据库:let bool = closeDB(db)
    创建表:let bool = creatTable("T_Class")
    查询:let sql = "SELECT * FROM T_Class;"
         let res = SQLiteManager.shareManager().execRecordSQL(sql)
    删除(更新插入修改下sql语句):
         let sql = "DELETE FROM T_Class WHERE age IS 10;"
         let bool = SQLiteManager.shareManager().execSQL(sql)
    子线程执行插入数据库:
    SQLiteManager.shareManager().execQueueSQL { (manager) -> () in
        // 1.编写SQL语句
        let sql = "INSERT INTO T_Person" +
            "(name, age)" +
            "VALUES" +
        "('\(self.name!)', \(self.age));"
        // 2.执行SQL语句
        manager.execSQL(sql)
        print(NSThread.currentThread())
    }
    事务+预编译 提高效率:
    let manager = SQLiteManager.shareManager()
    // 开启事务
     manager.beginTransaction()
    for i in 0..<10000
    {
        let sql = "INSERT INTO T_Person" +
            "(name, age)" +
            "VALUES" +
            "(?, ?);"
        manager.batchExecSQL(sql, args: "yy +\(i)", 1 + i)
    }
    // 提交事务
    manager.commitTransaction()

    class SQLiteManager: NSObject {
        
        private static let manager: SQLiteManager = SQLiteManager()
        /// 单粒
        class func shareManager() ->SQLiteManager {
            return manager
        }
        
        // 数据库对象
        private var db:COpaquePointer = nil
        
        /**
         打开数据库
         
         :param: SQLiteName 数据库名称
         */
        func openDB(SQLiteName: String)
        {
            // 0.拿到数据库的路径
            let path = SQLiteName.docDir()
            print(path)
            let cPath = path.cStringUsingEncoding(NSUTF8StringEncoding)!
            // 1.打开数据库
            /*
             1.需要打开的数据库文件的路径, C语言字符串
             2.打开之后的数据库对象 (指针), 以后所有的数据库操作, 都必须要拿到这个指针才能进行相关操作
             */
            // open方法特点: 如果指定路径对应的数据库文件已经存在, 就会直接打开
            //              如果指定路径对应的数据库文件不存在, 就会创建一个新的
            if sqlite3_open(cPath, &db) != SQLITE_OK
            {
                print("打开数据库失败")
                return
            }

        // 创建一个串行队列
        private let dbQueue = dispatch_queue_create("com.xxx.sql", DISPATCH_QUEUE_SERIAL)
        /**
         
         子线程执行数据库插入操作
         - parameter action:闭包
         */
        func execQueueSQL(action: (manager: SQLiteManager)->())
        {
            // 1.开启一个子线程
            dispatch_async(dbQueue) { () -> Void in
                print(NSThread.currentThread())
                // 2.执行闭包
                action(manager: self)
            }
        }              

            // 2.创建表
            if creatTable("T_Class")
            {
                print("创建表成功")
            }else
            {
                print("创建表失败")
            }
        }

        /// 自定义一个SQLITE_TRANSIENT, 覆盖系统的
        private let SQLITE_TRANSIENT = unsafeBitCast(-1, sqlite3_destructor_type.self)
        // MARK: - 预编译 + 事务可以提高效率
        func batchExecSQL(sql:String, args: CVarArgType...) -> Bool
        {
            
            // 1.将SQL语句转换为C语言
            let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
            
            // 2.预编译SQL语句
            var stmt: COpaquePointer = 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.cStringUsingEncoding(NSUTF8StringEncoding)!
                    // 第三个参数: 需要绑定的字符串, 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 += 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
        }

        /**
         关闭数据库
         
         - parameter db: 数据库
         */
        func closeDB(db: COpaquePointer) -> Bool{
            if sqlite3_close(db) == SQLITE_OK {
                return true
            }
            return false
        }

        // MARK: - 事务相关 (可以提高性能)
        // 1.开启事务
        func beginTransaction()
        {
            execSQL("BEGIN TRANSACTION")
        }
        // 2.提交事务
        func commitTransaction()
        {
            execSQL("COMMIT TRANSACTION")
        }
        // 3.回滚
        func rollbackTransaction()
        {
            execSQL("ROLLBACK TRANSACTION")
        }

        //创建表
        func creatTable(table: String) -> Bool
        {
            // 1.编写SQL语句
            let sql = "CREATE TABLE IF NOT EXISTS \(table)( \n" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
                "name TEXT, \n" +
                "age INTEGER \n" +
            "); \n"
            // 2.执行SQL语句
            return execSQL(sql)
        }
        
        /**
         执行除查询以外的SQL语句
         
         :param: sql 需要执行的SQL语句
         
         :returns: 是否执行成功 true执行成功 false执行失败
         */
        func execSQL(sql: String) -> Bool
        {
            // 0.将Swift字符串转换为C语言字符串
            let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
            
            // 在SQLite3中, 除了查询意外(创建/删除/新增/更新)都使用同一个函数
            /*
             1. 已经打开的数据库对象
             2. 需要执行的SQL语句, C语言字符串
             3. 执行SQL语句之后的回调, 一般传nil
             4. 是第三个参数的第一个参数, 一般传nil
             5. 错误信息, 一般传nil
             */
            if sqlite3_exec(db, cSQL, nil, nil, nil) != SQLITE_OK
            {
                return false
            }
            return true
        }
        
        /**
         查询所有的数据
         :returns: 查询到的字典数组
         */
        func execRecordSQL(sql: String) ->[[String: AnyObject]]
        {
            // 0.将Swift字符串转换为C语言字符串
            let cSQL = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
            
            // 1.准备数据
            // 准备: 理解为预编译SQL语句, 检测里面是否有错误等等, 它可以提供性能
            /*
             1.已经开打的数据库对象
             2.需要执行的SQL语句
             3.需要执行的SQL语句的长度, 传入-1系统自动计算
             4.预编译之后的句柄, 已经要想取出数据, 就需要这个句柄
             5. 一般传nil
             */
            var stmt: COpaquePointer = 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)
                // 将当前获取到的这一条记录添加到数组中
                records.append(record)
            }
            // 注意点: 只要用到了stmt, 一定要关闭
            sqlite3_finalize(stmt)
            // 返回查询到的数据
            return records
        }
        
        /**
         获取一条记录的值
         
         :param: stmt 预编译好的SQL语句
         
         :returns: 字典
         */
        private func recordWithStmt(stmt: COpaquePointer) ->[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)!
                //                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)
                case SQLITE_FLOAT:
                    // 浮点型
                    let double = sqlite3_column_double(stmt, index)
                    record[name] = Double(double)
                case SQLITE3_TEXT:
                    // 文本类型
                    let cText = UnsafePointer<Int8>(sqlite3_column_text(stmt, index))
                    let text = NSString(CString: cText, encoding: NSUTF8StringEncoding)!
                    record[name] = text
                case SQLITE_NULL:
                    // 空类型
                    record[name] = NSNull()
                    print(record)
                default:
                    // 二进制类型 SQLITE_BLOB
                    // 一般情况下, 不会往数据库中存储二进制数据
                    print("")
                }
            }
            return record
        }
    }

  • SQLite函数总结

      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);  // 字符串数据
    

  • SQLite语句

      /*简单约束*/
      CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER);
      CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER NOT NULL);
      CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, age INTEGER);
      CREATE TABLE IF NOT EXISTS t_student(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER DEFAULT 1);
      
      /*分页*/
      SELECT * FROM t_student ORDER BY id ASC LIMIT 30, 10;
      
      /*排序*/
      SELECT * FROM t_student WHERE score > 50 ORDER BY age DESC;
      SELECT * FROM t_student WHERE score < 50 ORDER BY age ASC , score DESC;
      
      /*计量*/
      SELECT COUNT(*) FROM t_student WHERE age > 50;
      
      /*别名*/
      SELECT name as myName, age as myAge, score as myScore FROM t_student;
      SELECT name myName, age myAge, score myScore FROM t_student;
      SELECT s.name myName, s.age myAge, s.score myScore FROM t_student s WHERE s.age > 50;
      
      /*查询*/
      SELECT name, age, score FROM t_student;
      SELECT * FROM t_student;
      
      /*修改指定数据*/
      UPDATE t_student SET name = 'MM' WHERE age = 10;
      UPDATE t_student SET name = 'WW' WHERE age is 7;
      UPDATE t_student SET name = 'XXOO' WHERE age < 20;
      UPDATE t_student SET name = 'NNMM' WHERE age < 50 and score > 10;
      
      /*删除数据*/
      DELETE FROM t_student;
      
      /*更新数据*/
      UPDATE t_student SET name = 'LNJ';
      
      /*插入数据*/
      
      INSERT INTO t_student(age, score, name) VALUES ('28', 100, 'jonathan');
      INSERT INTO t_student(name, age) VALUES ('lee', '28');
      INSERT INTO t_student(score) VALUES (100);
      
      /*插入数据*/
      INSERT INTO t_student(name, age, score) VALUES ('lee', '28', 100);
      
      /*添加主键*/
      CREATE TABLE IF NOT EXISTS t_student (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, score REAL);
      /*添加主键*/
      CREATE TABLE IF NOT EXISTS t_student (id INTEGER, name TEXT, age INTEGER, score REAL, PRIMARY KEY(id));
      
      /*删除表*/
      DROP TABLE IF EXISTS t_student;
      
      /*创建表*/
      CREATE TABLE IF NOT EXISTS t_student(id INTEGER , name TEXT, age , score REAL);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容