亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MySQL > 正文

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

2024-07-24 13:14:07
字體:
來源:轉載
供稿:網友

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; i++) {    [colunmsString appendString:publicColumns[i]];    if (i != publicColumns.count-1) {     [colunmsString appendString:@", "];    }   }   NSMutableString* sql = [NSMutableString new];   [sql appendString:@"INSERT INTO "];   [sql appendString:newTableName];   [sql appendString:@"("];   [sql appendString:colunmsString];   [sql appendString:@")"];   [sql appendString:@" SELECT "];   [sql appendString:colunmsString];   [sql appendString:@" FROM "];   [sql appendFormat:@"%@_bak", newTableName];   [self.databaseQueue inDatabase:^(FMDatabase *db) {    [db executeUpdate:sql];   }];  }];  // 刪除備份表  [self.databaseQueue inDatabase:^(FMDatabase *db) {   [db beginTransaction];   for (NSString* oldTableName in existsTables) {    NSString* sql = [NSString stringWithFormat:@"DROP TABLE IF EXISTS %@", oldTableName];    [db executeUpdate:sql];   }   [db commit];  }];  MMUserDefault.dbVersion = version_new; } else {  MMUserDefault.dbVersion = version_new; }}- (NSDictionary*)generateMigrationInfosWithOldTables:(NSArray*)oldTables newTables:(NSArray*)newTables { NSMutableDictionary<NSString*, NSArray* >* 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;}

問題

sqlite 刪除表文件的大小不變的問題

如有疑問請留言或者到本站社區交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!


注:相關教程知識閱讀請移步到MYSQL教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91成人在线视频| 国产精品第一视频| 欧美日韩国产精品一区二区三区四区| 成人免费直播live| 日韩免费不卡av| 91久久精品国产91性色| 亚洲精品久久久久中文字幕二区| 日韩高清电影好看的电视剧电影| 亚洲人成在线播放| 亚洲精品成人久久| 日韩欧美黄色动漫| 91久久久久久国产精品| 国产在线精品播放| 亚洲成人精品视频| 中文字幕亚洲精品| 久久免费在线观看| 国产美女精品视频免费观看| 亚洲人成电影网站色…| 亚洲男人天天操| 日本a级片电影一区二区| 国产精品自拍偷拍视频| 欧美黑人性视频| 欧美xxxx做受欧美| 日韩精品在线视频观看| 成人国产在线激情| 久久久久亚洲精品国产| 国产精品美女视频网站| 一区国产精品视频| 久久的精品视频| 精品国产区一区二区三区在线观看| 欧美性猛交xxxx黑人猛交| 国产一区二区三区在线播放免费观看| 日韩中文字幕网站| 久久久av一区| 91精品久久久久| 亚洲精品资源美女情侣酒店| 日韩一二三在线视频播| 狠狠色噜噜狠狠狠狠97| 欧美日韩黄色大片| 久久视频在线视频| www.99久久热国产日韩欧美.com| 久久亚洲国产精品| 久久久国产精品视频| 欧美精品aaa| 久久99国产精品自在自在app| 国产精品视频成人| 成人在线小视频| 亚洲电影中文字幕| 91精品啪aⅴ在线观看国产| 91免费国产视频| 欧美大秀在线观看| 日韩精品在线第一页| 日韩精品免费在线播放| 久久久久久久网站| 日韩av不卡在线| 日韩欧美在线免费| 日韩av高清不卡| 国产又爽又黄的激情精品视频| 精品调教chinesegay| 国产精品无av码在线观看| 亚洲第一免费播放区| 中文字幕日韩精品在线观看| 国产精品久久久久久久久久东京| 久久69精品久久久久久国产越南| 国产精品高潮呻吟久久av无限| 岛国视频午夜一区免费在线观看| 成人免费视频在线观看超级碰| 欧美日韩中国免费专区在线看| 2019最新中文字幕| 亚洲精品美女久久| 精品香蕉在线观看视频一| 这里只有精品在线播放| 久久久av一区| 久久久久久久一| 久久这里有精品视频| 岛国av午夜精品| 97久久超碰福利国产精品…| 欧美一级视频在线观看| 日韩精品有码在线观看| 国产精品欧美一区二区三区奶水| 自拍偷拍亚洲精品| 黑人精品xxx一区| 欧美综合国产精品久久丁香| 国产精品国产自产拍高清av水多| 欧美老女人性生活| 亚洲精品www久久久| 日韩大胆人体377p| 国产精品扒开腿做爽爽爽的视频| 7777精品久久久久久| 欧美一区二三区| 久久99久久久久久久噜噜| 亚洲人成在线播放| 秋霞午夜一区二区| 国产成人综合精品| 欧美噜噜久久久xxx| 亚洲图片在线综合| 国产精品一二三视频| 亚洲一级免费视频| 免费91麻豆精品国产自产在线观看| 日韩精品在线观看一区| 亚洲国产精品女人久久久| 69视频在线播放| 亚洲欧洲国产伦综合| 国模精品系列视频| 亚洲人成网站在线播| 日韩经典中文字幕在线观看| 亚洲天堂第二页| 亚洲精品乱码久久久久久金桔影视| 国产亚洲欧洲在线| 欧美丝袜一区二区| 亚洲一区国产精品| 亚洲国产成人精品女人久久久| 97超碰国产精品女人人人爽| 精品av在线播放| 色樱桃影院亚洲精品影院| 亚洲欧美国产精品专区久久| 久久高清视频免费| 性亚洲最疯狂xxxx高清| 中文在线不卡视频| 国产亚洲美女久久| 国产欧美日韩中文| 久久99国产精品自在自在app| 一本色道久久综合亚洲精品小说| 一区二区三区国产视频| 黑人巨大精品欧美一区二区三区| 国产亚洲精品久久久| 日韩精品中文字幕在线观看| 欧美噜噜久久久xxx| 911国产网站尤物在线观看| 日韩精品免费电影| 亚洲最大av网站| 97精品一区二区视频在线观看| 亚洲国产精品久久精品怡红院| www.色综合| 97热在线精品视频在线观看| 久久黄色av网站| 国产精品毛片a∨一区二区三区|国| 亚洲欧美日韩综合| 97精品一区二区视频在线观看| 成人h猎奇视频网站| 色yeye香蕉凹凸一区二区av| 国产午夜精品视频| 国产精品主播视频| 国产成+人+综合+亚洲欧美丁香花| 欧美激情视频给我| 热久久免费视频精品| 日韩精品极品视频免费观看| 欧美麻豆久久久久久中文| 日韩在线观看免费高清完整版| 精品久久久久久电影| 欧美成人免费视频| 热门国产精品亚洲第一区在线| 伊人一区二区三区久久精品| 日韩中文字幕欧美| 2020久久国产精品| 久久久久国产视频| 亚洲精品久久久久久久久久久| 日韩不卡在线观看| 亚洲国产精品va在线看黑人| 久久久精品美女| 久久影视电视剧免费网站清宫辞电视| 成人亚洲综合色就1024| 7m精品福利视频导航| 色久欧美在线视频观看|