什么是SQLite
什么是数据库
良好的数据库编程规范应该要保证每条记录的唯一性,为此,增加了主键约束
什么是主键
主键的设计原则
主键的声明
什么是SQL语句
SQL语句的特点
数据库中不可以使用关键字来命名表、字段
SQL语句的种类
// 示例
select count (age) from t_student ;
select count ( * ) from t_student where score >= 60;
// 示例
select * from t_student limit 4, 8 ;
// 可以理解为:跳过最前面4条语句,然后取8条记录
// 示例
create table t_student (id integer, name text not null unique, age integer not null default 1) ;
// name字段不能为null,并且唯一
// age字段不能为null,并且默认为1
示例
create table t_student (id integer, name text, age inetger, score real) ;
示例
drop table t_student ;
// 将t_student表中年龄大于10 并且 姓名不等于jack的记录,年龄都改为 5
update t_student set age = 5 where age > 10 and name != ‘jack’ ;
// 删除t_student表中年龄小于等于10 或者 年龄大于30的记录
delete from t_student where age <= 10 or age > 30 ;
示例
select name, age from t_student ;
select * from t_student ;
select * from t_student where age > 10 ; // 条件查询
// 示例
select name myname, age myage from t_student ;
// 给name起个叫做myname的别名,给age起个叫做myage的别名
select s.name, s.age from t_student s ;
// 给t_student表起个别名叫做s,利用s来引用表中的字段
create table t_student (id integer primary key autoincrement, name text, age integer, class_id integer, constraint fk_t_student_class_id_t_class_id foreign key (class_id) references t_class (id)) ;
// t_student表中有一个叫做fk_t_student_class_id_t_class_id的外键
// 这个外键的作用是用t_student表中的class_id字段引用t_class表的id字段
如果导入(tbd 文件)后报错解决方法
SQLite是基于 C语言,跟 swift 需要桥接
使用数据库先搞一个单例(SQLiteManager.swift)
class SQLiteManager: NSObject {
static let shareInstance: SQLiteManager = SQLiteManager()
}
/**打开指定名称的数据库, 如果不存在就创建一个新的 - parameter name: 数据库文件的名称 */
func openDatabase(name: String)
{
// 1.生成数据库文件的路径
let path = name.documentDir()
let cPath = path.cStringUsingEncoding(NSUTF8StringEncoding)!
/* sqlite3_open方法可以打开已经已经存在的数据库, 如果数据库不存在就会自动创建一个新的 1.需要打开的数据库文件的绝对路径 2.数据库句柄(指针), 以后所有对数据库的操作都需要依赖这个指针 */
if sqlite3_open(cPath, &db) != SQLITE_OK
{
print("打开数据库失败")
return
}
/* 1.企业开发中, SQL语句最好现在PC端验证之后再拷贝过来 2.如果SQL语句太长, 建议换行, 方便调试 */
let sql = "CREATE TABLE IF NOT EXISTS T_Person( \n" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, \n" +
"name TEXT, \n" +
"age INTEGER \n" +
");"
if !createTable(sql)
{
print("创建表失败")
return
}
/** 创建表 */
func createTable(sql: String) -> Bool
{
return execSQL(sql)
}
func execSQL(sql: String) -> Bool
{
let cSql = sql.cStringUsingEncoding(NSUTF8StringEncoding)!
/* sqlite3_exec执行一条SQL语句 第一个参数: 一个已经打开的数据库句柄 第二个参数: 需要执行的SQL语句(C语言字符串) 第三个参数: 回调函数(一般传nil) 第四个参数: 第三个参数的第一个参数(一般传nil) 第五个参数: 错误信息 */
return sqlite3_exec(db, cSql, nil, nil, nil) == SQLITE_OK
}
class Person: NSObject {
var id: Int = 0
var name: String?
var age: Int = 0
init(dict: [String: AnyObject])
{
super.init()
setValuesForKeysWithDictionary(dict)
}
override func setValue(value: AnyObject?, forUndefinedKey key: String) {
}
override var description: String {
let property = ["id", "name", "age"]
let dict = dictionaryWithValuesForKeys(property)
return "\(dict)"
}
/** 插入一条记录 */
func insertPerson() -> Bool
{
let sql = "INSERT INTO T_Person \n" +
"(name, age) \n" +
"VALUES \n" +
"('\(name!)', \(age));"
return SQLiteManager.shareInstance.execSQL(sql)
}
/** 跟新记录 - parameter name: 新值 */
func updatePerson(name: String) -> Bool
{
let sql = "UPDATE T_Person \n" +
"SET name = '\(name)' \n" +
"WHERE name IS '\(self.name!)';"
return SQLiteManager.shareInstance.execSQL(sql)
}
/** 删除一条记录 */
func deletePerson() -> Bool
{
let sql = "DELETE FROM T_Person WHERE age IS \(self.age);"
return SQLiteManager.shareInstance.execSQL(sql)
}
}
/**
查询数据
- parameter sql: 需要执行的语句
*/
func recordExecSQL(sql: String) -> [[String: AnyObject]]?
{
// 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 nil
}
// 3.取出每一行的数据
var res = [[String: AnyObject]]()
while sqlite3_step(stmt) == SQLITE_ROW
{
let record = recordWithStmt(stmt)
res.append(record)
}
// 释放结果集
sqlite3_finalize(stmt)
return res
}
/** 根据结果集查询一条数据 - parameter stmt: 结果集 - returns: 字典 */
private func recordWithStmt(stmt: COpaquePointer) -> [String: AnyObject]
{
// 定义字典, 保存当前行数据
var record = [String: AnyObject]()
// 1.取出当前行的总列数
let count = sqlite3_column_count(stmt)
for i in 0..<count
{
// 2.取出每一列的名称
let cName = sqlite3_column_name(stmt, i)
let name = String(CString: cName, encoding: NSUTF8StringEncoding)!
// 3.取出每一列的类型
let type = sqlite3_column_type(stmt, i)
// 4.判断当前列的数据类型
switch type
{
case SQLITE_INTEGER:
// 整型
let intValue = sqlite3_column_int64(stmt, i)
record[name] = Int(intValue)
case SQLITE_FLOAT:
// 浮点类型
let floatValue = sqlite3_column_double(stmt, i)
record[name] = floatValue
case SQLITE3_TEXT:
// 文本类型 UnsafePointer<UInt8>
let cText = UnsafePointer<Int8>(sqlite3_column_text(stmt, i))
let text = String(CString: cText, encoding: NSUTF8StringEncoding)!
record[name] = text
case SQLITE_NULL:
// 空
// print("空")
record[name] = NSNull()
default:
// 二进制
// 企业开发中, 一般不会将二进制存储到数据库中
print("二进制")
}
}
return record
}
var db: COpaquePointer = nil
/// 因为数据库是一个文件, 不能多条线程同时操作, 所以创建一个串行队列
var queue: dispatch_queue_t = dispatch_queue_create("com.baidu.n g", DISPATCH_QUEUE_SERIAL)
func insertAction(action: (manager: SQLiteManager)->()) { dispatch_async(queue) { () -> Void in
action(manager: self)
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print(NSThread.currentThread())
for i in 0..<20000
{
let p = Person(dict: ["name": "zs\(i + 100)", "age": 30 + i])
// print(p.insertPerson())
p.insertQueuePerson()
}
}
}
由于数据库是一个文件,不能多线程操作,必须搞一个专门的串行的子线程操作
/** 开启事务 */
func beginTransaction()
{
execSQL("BEGIN TRANSACTION;")
}
/** 提交事务 */
func commitTransaction()
{
execSQL("COMMIT TRANSACTION;")
}
/** 回滚事务 */
func rollbackTransaction()
{
execSQL("ROLLBACK TRANSACTION;")
}
一般情况下, 做数据的批量处理时都需要考虑回滚