本文主要介紹了ios 數據庫升級數據遷移的實例詳解的相關資料,這里提供實例幫助大家解決數據庫升級及數據遷移的問題,需要的朋友可以參考下,希望能幫助到大家。
IOS 數據庫升級數據遷移的實例詳解
概要:
很久以前就遇到過數據庫版本升級的引用場景,當時的做法是簡單的刪除舊的數據庫文件,重建數據庫和表結構,這種暴力升級的方式會導致舊的數據的丟失,現在看來這并不不是一個優雅的解決方案,現在一個新的項目中又使用到了數據庫,我不得不重新考慮這個問題,我希望用一種比較優雅的方式去解決這個問題,以后我們還會遇到類似的場景,我們都想做的更好不是嗎?
理想的情況是:數據庫升級,表結構、主鍵和約束有變化,新的表結構建立之后會自動的從舊的表檢索數據,相同的字段進行映射遷移數據,而絕大多數的業務場景下的數據庫版本升級是只涉及到字段的增減、修改主鍵約束,所以下面要實現的方案也是從最基本的、最常用的業務場景去做一個實現,至于更加復雜的場景,可以在此基礎上進行擴展,達到符合自己的預期的。
選型定型
網上搜索了下,并沒有數據庫升級數據遷移簡單完整的解決方案,找到了一些思路
1.清除舊的數據,重建表
優點:簡單
缺點:數據丟失
2.在已有表的基礎上對表結構進行修改
優點:能夠保留數據
缺點:規則比較繁瑣,要建立一個數據庫的字段配置文件,然后讀取配置文件,執行SQL修改表結構、約束和主鍵等等,涉及到跨多個版本的數據庫升級就變得繁瑣并且麻煩了
3.創建臨時表,把舊的數據拷貝到臨時表,然后刪除舊的數據表并且把臨時表設置為數據表。
優點:能夠保留數據,支持表結構的修改,約束、主鍵的變更,實現起來比較簡單
缺點:實現的步驟比較多
綜合考慮,第三種方法是一個比較靠譜的方案。
主要步驟
根據這個思路,分析了一下數據庫升級了主要步驟大概如下:
-
獲取數據庫中舊的表
-
修改表名,添加后綴“_bak”,把舊的表當做備份表
-
創建新的表
-
獲取新創建的表
-
遍歷舊的表和新表,對比取出需要遷移的表的字段
-
數據遷移處理
-
刪除備份表
使用到的SQL語句分析
這些操作都是和數據庫操作有關系的,所以問題的關鍵是對應步驟的SQL語句了,下面分析下用到的主要的SQL語句:
獲取數據庫中舊的表
SELECT?*?from?sqlite_master?WHERE?type='table'
結果如下,可以看到有type | name | tbl_name | rootpage | sql 這些數據庫字段,我們只要用到name也就是數據庫名稱這個字段就行了
sqlite>?SELECT?*?from?sqlite_master?WHERE?type='table' ?...>?; +-------+---------------+---------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |?type?|?name???|?tbl_name??|?rootpage?|?sql?????????????????????????????????????????????????????| +-------+---------------+---------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ |?table?|?t_message_bak?|?t_message_bak?|?2??|?CREATE?TABLE?"t_message_bak"?(messageID?TEXT,?messageType?INTEGER,?messageJsonContent?TEXT,?retriveTimeString?INTEGER,?postTimeString?INTEGER,?readState?INTEGER,?PRIMARY?KEY(messageID))????????| |?table?|?t_message??|?t_message??|?4??|?CREATE?TABLE?t_message?( ?messageID?TEXT,? ?messageType?INTEGER, ?messageJsonContent?TEXT,? ?retriveTimeString?INTEGER,? ?postTimeString?INTEGER,? ?readState?INTEGER,? ?addColumn?INTEGER, ?PRIMARY?KEY(messageID) )?| +-------+---------------+---------------+----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 2?行于數據集?(0.03?秒)
修改表名,添加后綴“_bak”,把舊的表當做備份表
--?把t_message表修改為t_message_bak表? ALTER?TABLE?t_message?RENAME?TO?t_message_bak
獲取表字段信息
--?獲取t_message_bak表的字段信息 PRAGMA?table_info('t_message_bak')
獲取到的表字段信息如下,可以看到有| cid | name | type | notnull | dflt_value | pk | 這些數據庫字段,我們只要用到name也就是字段名稱這個字段就行了
sqlite>?PRAGMA?table_info('t_message_bak'); +------+--------------------+---------+---------+------------+------+ |?cid?|?name????|?type?|?notnull?|?dflt_value?|?pk?| +------+--------------------+---------+---------+------------+------+ |?0?|?messageID???|?TEXT?|?0??|?NULL??|?1?| |?1?|?messageType??|?INTEGER?|?0??|?NULL??|?0?| |?2?|?messageJsonContent?|?TEXT?|?0??|?NULL??|?0?| |?3?|?retriveTimeString?|?INTEGER?|?0??|?NULL??|?0?| |?4?|?postTimeString??|?INTEGER?|?0??|?NULL??|?0?| |?5?|?readState???|?INTEGER?|?0??|?NULL??|?0?| +------+--------------------+---------+---------+------------+------+ 6?行于數據集?(0.01?秒)
使用子查詢進行數據遷移處理
INSERT?INTO?t_message(messageID,?messageType,?messageJsonContent,?retriveTimeString, ?postTimeString,?readState)?SELECT?messageID,?messageType,?messageJsonContent,?retriveTimeString, ?postTimeString,?readState?FROM?t_message_bak
把t_message_bak表中的messageID, messageType, messageJsonContent, retriveTimeString, postTimeString, readState這些字段的值復制到t_message表中
代碼實現
接下來就到了代碼的實現步驟了
//?創建新的臨時表,把數據導入臨時表,然后用臨時表替換原表 -?(void)baseDBVersionControl?{ ?NSString?*?version_old?=?ValueOrEmpty(MMUserDefault.dbVersion); ?NSString?*?version_new?=?[NSString?stringWithFormat:@"%@",?DB_Version]; ?NSLog(@"dbVersionControl?before:?%@?after:?%@",version_old,version_new); ?//?數據庫版本升級 ?if?(version_old?!=?nil?&&?![version_new?isEqualToString:version_old])?{ ??//?獲取數據庫中舊的表 ??NSArray*?existsTables?=?[self?sqliteExistsTables]; ??NSMutableArray*?tmpExistsTables?=?[NSMutableArray?array]; ??//?修改表名,添加后綴“_bak”,把舊的表當做備份表 ??for?(NSString*?tablename?in?existsTables)?{ ???[tmpExistsTables?addObject:[NSString?stringWithFormat:@"%@_bak",?tablename]]; ???[self.databaseQueue?inDatabase:^(FMDatabase?*db)?{ ????NSString*?sql?=?[NSString?stringWithFormat:@"ALTER?TABLE?%@?RENAME?TO?%@_bak",?tablename,?tablename]; ????[db?executeUpdate:sql]; ???}]; ??} ??existsTables?=?tmpExistsTables; ??//?創建新的表 ??[self?initTables]; ??//?獲取新創建的表 ??NSArray*?newAddedTables?=?[self?sqliteNewAddedTables]; ??//?遍歷舊的表和新表,對比取出需要遷移的表的字段 ??NSDictionary*?migrationInfos?=?[self?generateMigrationInfosWithOldTables:existsTables?newTables:newAddedTables]; ??//?數據遷移處理 ??[migrationInfos?enumerateKeysAndObjectsUsingBlock:^(NSString*?newTableName,?NSArray*?publicColumns,?BOOL?*?_Nonnull?stop)?{ ???NSMutableString*?colunmsString?=?[NSMutableString?new]; ???for?(int?i?=?0;?i<publiccolumns.count>*?migrationInfos?=?[NSMutableDictionary?dictionary]; ?for?(NSString*?newTableName?in?newTables)?{ ??NSString*?oldTableName?=?[NSString?stringWithFormat:@"%@_bak",?newTableName]; ??if?([oldTables?containsObject:oldTableName])?{ ???//?獲取表數據庫字段信息 ???NSArray*?oldTableColumns?=?[self?sqliteTableColumnsWithTableName:oldTableName]; ???NSArray*?newTableColumns?=?[self?sqliteTableColumnsWithTableName:newTableName]; ???NSArray*?publicColumns?=?[self?publicColumnsWithOldTableColumns:oldTableColumns?newTableColumns:newTableColumns]; ???if?(publicColumns.count?>?0)?{ ????[migrationInfos?setObject:publicColumns?forKey:newTableName]; ???} ??} ?} ?return?migrationInfos; } -?(NSArray*)publicColumnsWithOldTableColumns:(NSArray*)oldTableColumns?newTableColumns:(NSArray*)newTableColumns?{ ?NSMutableArray*?publicColumns?=?[NSMutableArray?array]; ?for?(NSString*?oldTableColumn?in?oldTableColumns)?{ ??if?([newTableColumns?containsObject:oldTableColumn])?{ ???[publicColumns?addObject:oldTableColumn]; ??} ?} ?return?publicColumns; } -?(NSArray*)sqliteTableColumnsWithTableName:(NSString*)tableName?{ ?__block?NSMutableArray<nsstring>*?tableColumes?=?[NSMutableArray?array]; ?[self.databaseQueue?inDatabase:^(FMDatabase?*db)?{ ??NSString*?sql?=?[NSString?stringWithFormat:@"PRAGMA?table_info('%@')",?tableName]; ??FMResultSet?*rs?=?[db?executeQuery:sql]; ??while?([rs?next])?{ ???NSString*?columnName?=?[rs?stringForColumn:@"name"]; ???[tableColumes?addObject:columnName]; ??} ?}]; ?return?tableColumes; } -?(NSArray*)sqliteExistsTables?{ ?__block?NSMutableArray<nsstring>*?existsTables?=?[NSMutableArray?array]; ?[self.databaseQueue?inDatabase:^(FMDatabase?*db)?{ ??NSString*?sql?=?@"SELECT?*?from?sqlite_master?WHERE?type='table'"; ??FMResultSet?*rs?=?[db?executeQuery:sql]; ??while?([rs?next])?{ ???NSString*?tablename?=?[rs?stringForColumn:@"name"]; ???[existsTables?addObject:tablename]; ??} ?}]; ?return?existsTables; } -?(NSArray*)sqliteNewAddedTables?{ ?__block?NSMutableArray<nsstring>*?newAddedTables?=?[NSMutableArray?array]; ?[self.databaseQueue?inDatabase:^(FMDatabase?*db)?{ ??NSString*?sql?=?@"SELECT?*?from?sqlite_master?WHERE?type='table'?AND?name?NOT?LIKE?'%_bak'"; ??FMResultSet?*rs?=?[db?executeQuery:sql]; ??while?([rs?next])?{ ???NSString*?tablename?=?[rs?stringForColumn:@"name"]; ???[newAddedTables?addObject:tablename]; ??} ?}]; ?return?newAddedTables; }</nsstring></nsstring></nsstring></publiccolumns.count>
相關推薦:
sql 2005?數據庫升級2008 數據庫 和2005 數據附加2008數據備份文