<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      案例詳細說明IOS 數據庫升級數據遷移

      [摘要]本文主要介紹了IOS 數據庫升級數據遷移的實例詳解的相關資料,這里提供實例幫助大家解決數據庫升級及數據遷移的問題,需要的朋友可以參考下,希望能幫助到大家。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 && ![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;
      }

      相關推薦:

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

      SQL server 數據庫升級版本問題解決辦法

      詳解oracle數據庫遷移到MySQL的方法總結(圖文)

      以上就是實例詳解IOS 數據庫升級數據遷移的詳細內容,更多請關注php中文網其它相關文章!


      學習教程快速掌握從入門到精通的SQL知識。




      日韩精品一区二区三区高清