compareDb.swift
呼叫方式:
compareDbVersion(iDbName: "db",iDbType: "sqlite")
如果想知道原理可以看以下說明:
以下是寫的流程:
1. 我們將利用比對的方式去查資料庫的資料是否有不一樣。
if(getLocalDbVersion() != getNewDbVersion())
{
compareDB()
//最後修改版本
update(updateStatementString: "UPDATE dbVersion SET version='\(getNewDbVersion())'")
}else{
print("資料庫版本一樣:\(dbVersion)|\(getNewDbVersion())")
}
2.可以先檢查兩個資料庫版本的不一樣「本機資料庫」、「新的資料庫」。
假設,你的資料庫是叫 db.sqlite2.1 在「本機資料庫」開啟的方式,這個只能用iTunes才看得到。
//開啟本機資料庫
func openLocalDatabase() -> OpaquePointer? {
var db: OpaquePointer? = nil
let dirPaths = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)
let docsDir:String! = dirPaths[0]
let destPath = (docsDir as NSString).appendingPathComponent("/db.sqlite")
if sqlite3_open(destPath, &db) == SQLITE_OK {
//print("Successfully opened connection to database at \(destPath)")
return db
}
return nil
}
2.2 「新的資料庫」開啟的方式,這個的意思是你放在app裡面的db.sqlite,如圖1:
//開啟新的sqlite資料
func openNewDatabase() -> OpaquePointer?{
var db: OpaquePointer? = nil
let path = Bundle.main.path(forResource: "db", ofType:"sqlite")
if sqlite3_open(path, &db) == SQLITE_OK {
//print("Successfully opened connection to database at \(path)")
return db
} else {
}
return nil
}
圖1、App的db.sqlite |
3.判斷這兩個版本是否一樣,不一樣的話,才進行「第4步」之後的比對動作。以下是取得「本機資料庫」和「新的資料庫」版本方式
3.1 「本機資料庫」版本的程式
//取得本機版本
func getLocalDbVersion()->String{
var Version = ""
var db = openLocalDatabase()
var queryStatementString = "SELECT version FROM dbVersion"
var queryStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
if sqlite3_step(queryStatement) == SQLITE_ROW {
let queryResultCol1 = sqlite3_column_text(queryStatement, 0)
Version = String(cString: queryResultCol1!)
} else {
print("Query returned no results")
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
return Version
}
3.2 「新的資料庫」版本的程式
//取得新的資料庫的資料
func getNewDbVersion()->String
{
var Version = ""
var db = openNewDatabase()
var queryStatementString = "SELECT version FROM dbVersion"
var queryStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
if sqlite3_step(queryStatement) == SQLITE_ROW {
let queryResultCol1 = sqlite3_column_text(queryStatement, 0)
Version = String(cString: queryResultCol1!)
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
return Version
}
4.當版本的比對不一樣時,我們就開始以下的動作。
4.1 利用 SELECT name, sql FROM sqlite_master WHERE type='table' 取得「新的資料庫」各「資料表的名稱」和「Create Table SQL語法」,圖2。
4.2 利用迴圈查詢「本機資料庫」的資料表是否和「新的資料表」的資料表相符的資料,語法:SELECT name FROM sqlite_master WHERE type='table' AND name='\(name)'。如果符合,就繼續「檢查欄位」(4.5),如果沒有,就直接利用「Create Table SQL語法」建立新得資料表(4.3、4.4)。
圖2 |
4.3 比對資料表與欄位的程式碼
//當資料版本不一樣的時候,會進行,比對db
func compareDB()
{
var dbLocal = openLocalDatabase()//手機本身的sqlite
var dbNew = openNewDatabase()//新的資料庫
var queryStatementString = "SELECT name, sql FROM sqlite_master WHERE type='table'"
var queryStatementLocal: OpaquePointer? = nil
var queryStatementNew: OpaquePointer? = nil
//取得新資料庫的資料表名稱和Crate Table SQL語法
if sqlite3_prepare_v2(dbNew, queryStatementString, -1, &queryStatementNew, nil) == SQLITE_OK {
//
while sqlite3_step(queryStatementNew) == SQLITE_ROW {
//取得「新資料庫」資料表名稱
let queryResultCol1 = sqlite3_column_text(queryStatementNew, 0)
let name = String(cString: queryResultCol1!)
//開始比對「本機資料庫」是否有相對應的資料表
queryStatementString = "SELECT name FROM sqlite_master WHERE type='table' AND name='\(name)'"
//
if sqlite3_prepare_v2(dbLocal, queryStatementString, -1, &queryStatementLocal, nil) == SQLITE_OK {
if sqlite3_step(queryStatementLocal) == SQLITE_ROW {
//如果「本機資料庫」有該比資料,那就再去比對「欄位」
compareColumn(tableName: name)
}else{
//如果「本機資料庫」沒有該資料表,那就取得Create Table SQL語法,建立新的
let queryResultCol1 = sqlite3_column_text(queryStatementLocal, 1)
let createTableString = String(cString: queryResultCol1!)
createTable(createTableString: createTableString)
}
}
}
} else {
print("SELECT statement could not be prepared")
}
// 6
sqlite3_finalize(queryStatementNew)
sqlite3_finalize(queryStatementLocal)
}
4.3 建立資料表的Swift寫法如下:
func createTable(createTableString:String) {
var db = openLocalDatabase()
var createTableStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
if sqlite3_step(createTableStatement) == SQLITE_DONE {
print("Contact table created.")
} else {
print("Contact table could not be created.")
}
} else {
print("CREATE TABLE statement could not be prepared.")
}
sqlite3_finalize(createTableStatement)
}
4.4 比對欄位的寫法:
//比較欄位
func compareColumn(tableName:String)
{
var dbNew = openNewDatabase()
var queryStatementString = "pragma table_info(\(tableName))"
var queryStatementNew: OpaquePointer? = nil
//取得新的資料表的資料欄位
if sqlite3_prepare_v2(dbNew, queryStatementString, -1, &queryStatementNew, nil) == SQLITE_OK {
while sqlite3_step(queryStatementNew) == SQLITE_ROW {
//取得所有欄位名稱
let queryResultCol1 = sqlite3_column_text(queryStatementNew, 1)
let fieldName = String(cString: queryResultCol1!)
//判斷本機資料庫欄位是否存在
if(!isFieldExist(tableName: tableName, fieldName: fieldName))
{
//如果不存在,就在該資料表建立新的欄位
let queryResultCol1 = sqlite3_column_text(queryStatementNew, 1)
let col1 = String(cString: queryResultCol1!)
let queryResultCol2 = sqlite3_column_text(queryStatementNew, 2)
let col2 = String(cString: queryResultCol2!)
let col3 = sqlite3_column_int(queryStatementNew, 3)
let col5 = sqlite3_column_int(queryStatementNew, 5)
var sql = "ALTER TABLE \(tableName) ADD COLUMN \(col1) \(col2) ";
if col3 == 1 {
sql = sql + " not null"
}
if col5 == 1 {
sql = sql + " PRIMARY KEY(\(tableName))";
}
//建立新的欄位
createTable(createTableString: sql)
}else{
print("表格:\(tableName),欄位:\(fieldName) 存在")
}
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatementNew)
}
4.5 檢查「本機資料庫」是否有相對應的欄位:
//檢查本機是否有這個欄位
func isFieldExist(tableName:String,fieldName:String)->Bool{
var isExist :Bool = false
var dbLocal = openLocalDatabase()
var queryStatementString = "pragma table_info(\(tableName))"
var queryStatementLocal: OpaquePointer? = nil
if sqlite3_prepare_v2(dbLocal, queryStatementString, -1, &queryStatementLocal, nil) == SQLITE_OK {
while sqlite3_step(queryStatementLocal) == SQLITE_ROW {
//檢查「本機資料庫」所有欄位名稱
let queryResultCol1 = sqlite3_column_text(queryStatementLocal,1)
let field = String(cString: queryResultCol1!)
if fieldName == field
{
//如果一樣的欄位就直接離開。
isExist = true
break
}
}
}
sqlite3_finalize(queryStatementLocal)
return isExist
}
5.最後、最後,更新本機端的版本號
檢查完畢後,取得「新的資料庫」版本號,進行「本機資料庫」的更新。程式碼如下:
//最後修改版本
update(updateStatementString: "UPDATE dbVersion SET version='(getNewDbVersion())'")
func update(updateStatementString:String) {
var db = openLocalDatabase()
var updateStatement: OpaquePointer? = nil
if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
if sqlite3_step(updateStatement) == SQLITE_DONE {
print("Successfully updated row.")
} else {
print("Could not update row.")
}
} else {
print("UPDATE statement could not be prepared")
}
sqlite3_finalize(updateStatement)
}
沒有留言:
張貼留言