實例詳解IOS 數據庫升級數據遷移

本文主要介紹了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?&amp;&amp;?![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?&gt;?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數據備份文

sql 2005?數據庫升級2008 數據庫 和2005 數據附加2008數據備份文

sql 2005?數據庫升級2008 數據庫 和2005 數據附加2008數據備份文

? 版權聲明
THE END
喜歡就支持一下吧
點贊11 分享