IOSデータベースアップグレードデータ移行の詳細な例 まとめ: 昔、データベースのバージョン アップグレードの参考シナリオに遭遇しました。当時のアプローチは、古いデータベース ファイルを削除し、データベースとテーブル構造を再構築するだけでした。この乱暴なアップグレード方法では、古いデータが失われます。今では、これはエレガントな解決策ではないようです。現在、新しいプロジェクトでデータベースが再び使用されるため、この問題を再検討する必要があります。この問題をよりエレガントな方法で解決したいと考えています。今後も同様のシナリオに遭遇するでしょうが、私たちは皆、より良い方法を望んでいますよね? 理想的な状況は、データベースをアップグレードすると、テーブル構造、主キー、制約が変更されることです。新しいテーブル構造が確立されると、古いテーブルからデータが自動的に取得され、同じフィールドがマッピングされて移行されます。ほとんどのビジネス シナリオでは、データベース バージョンのアップグレードには、フィールドの追加または削除と主キー制約の変更のみが含まれます。したがって、以下で実装するソリューションも、最も基本的で最も一般的に使用されるビジネス シナリオに基づいて実装することです。より複雑なシナリオについては、これに基づいて拡張して期待に応えることができます。 選択と確定 オンラインで検索したところ、データベースのアップグレードとデータ移行のためのシンプルで完全なソリューションは見つかりませんでした。いくつかのアイデアを見つけました。 1. 古いデータを消去してテーブルを再構築する 長所: シンプル 短所: データ損失 2. 既存のテーブルに基づいてテーブル構造を変更する メリット: データを保持できる デメリット: ルールが比較的複雑です。データベース フィールド構成ファイルを作成し、構成ファイルを読み込み、SQL を実行してテーブル構造、制約、主キーなどを変更する必要があります。複数のバージョンにまたがってデータベースをアップグレードするのは面倒で面倒です。 3. 一時テーブルを作成し、古いデータを一時テーブルにコピーしてから、古いデータ テーブルを削除し、一時テーブルをデータ テーブルとして設定します。 利点: データを保持できる、テーブル構造の変更をサポート、制約と主キーの変更をサポート、実装が比較的簡単 欠点: 実装に多くの手順が必要 すべての要素を考慮すると、3 番目の方法の方が信頼性の高い解決策です。 主な手順 この考えに基づいて、データベース アップグレードの主な手順は次のように分析されます。
使用されたSQL文の分析 これらの操作はすべてデータベース操作に関連しているため、問題の鍵となるのは対応するステップの SQL ステートメントです。以下は、使用される主な SQL ステートメントの分析です。 データベース内の古いテーブルを取得する sqlite_master から * を選択 WHERE type='table' 結果は次のようになります。type | name | tbl_name | rootpage | sql などのデータベース フィールドがあることがわかります。使用する必要があるのは、データベース名である name フィールドだけです。 sqlite> SELECT * from sqlite_master WHERE type='table' ...> ; +-------+---------------+---------------+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | タイプ | 名前 | tbl_name | ルートページ | sql | +-------+---------------+---------------+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | テーブル | t_message_bak | t_message_bak | 2 | テーブル「t_message_bak」を作成します (messageID TEXT、messageType INTEGER、messageJsonContent TEXT、retriveTimeString INTEGER、postTimeString INTEGER、readState INTEGER、PRIMARY KEY(messageID)) | | テーブル | t_message | t_message | 4 | CREATE TABLE t_message ( メッセージIDテキスト、 メッセージタイプ INTEGER、 messageJsonContent テキスト、 取得時間文字列 INTEGER、 postTimeString INTEGER、 読み取り状態 INTEGER、 列の追加 INTEGER、 主キー(メッセージID) ) | +-------+---------------+---------------+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ データセット内の 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> プラグマ table_info('t_message_bak'); +------+--------------------+----------+----------+----------+------+ | cid | 名前 | タイプ | notnull | dflt_value | pk | +------+--------------------+----------+----------+----------+------+ | 0 | メッセージID | テキスト | 0 | NULL | 1 | | 1 | メッセージタイプ | INTEGER | 0 | NULL | 0 | | 2 | messageJsonContent | テキスト | 0 | NULL | 0 | | 3 | 取得時間文字列 | INTEGER | 0 | NULL | 0 | | 4 | postTimeString | INTEGER | 0 | NULL | 0 | | 5 | 読み取り状態 | 整数 | 0 | NULL | 0 | +------+--------------------+----------+----------+----------+------+ データセットの6行(0.01秒) データ移行にサブクエリを使用する t_messageに挿入(メッセージID、メッセージタイプ、メッセージJsonContent、取得時間文字列、 postTimeString、readState) SELECT messageID、messageType、messageJsonContent、retriveTimeString、 postTimeString、t_message_bak からの readState 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 前: %@ 後: %@",version_old,version_new); // データベースバージョンのアップグレード if (version_old != nil && ![version_new isEqualToString:version_old]) { // データベース内の古いテーブルを取得します NSArray* existsTables = [self sqliteExistsTables]; NSMutableArray* tmpExistsTables = [NSMutableArray 配列]; // テーブル名を変更し、サフィックス「_bak」を追加し、古いテーブルをバックアップテーブルとして使用します (NSString* tablename in existsTables) { [tmpExistsTables addObject:[NSString stringWithFormat:@"%@_bak", テーブル名]]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = [NSString stringWithFormat:@"ALTER TABLE %@ RENAME TO %@_bak", テーブル名, テーブル名]; [db 実行更新:sql]; }]; } 存在するテーブル = tmpExistsTables; // 新しいテーブルを作成します [self initTables]; // 新しく作成されたテーブルを取得します NSArray* newAddedTables = [self sqliteNewAddedTables]; // 古いテーブルと新しいテーブルを走査し、移行する必要があるテーブルのフィールドを比較して抽出します NSDictionary* migrationInfos = [self generateMigrationInfosWithOldTables:existsTables newTables:newAddedTables]; // データ移行処理 [migrationInfos enumerateKeysAndObjectsUsingBlock:^(NSString* newTableName, NSArray* publicColumns, BOOL * _Nonnull stop) { NSMutableString* 列文字列 = [NSMutableString new]; (int i = 0; i<publicColumns.count; i++) の場合 { [列文字列 appendString:publicColumns[i]]; if (i != publicColumns.count-1) { [列文字列追加文字列:@", "]; } } NSMutableString* sql = [NSMutableString new]; [sql appendString:@"INSERT INTO "]; [sql 追加文字列:新しいテーブル名]; [sql 追加文字列:@"("]; [sql 追加文字列:列文字列]; [sql 追加文字列:@")"]; [sql 追加文字列:@" SELECT "]; [sql 追加文字列:列文字列]; [sql appendString:@" FROM "]; [sql 追加フォーマット:@"%@_bak", 新しいテーブル名]; [self.databaseQueue inDatabase:^(FMDatabase *db) { [db 実行更新:sql]; }]; }]; // バックアップテーブルを削除します [self.databaseQueue inDatabase:^(FMDatabase *db) { [db トランザクション開始]; (NSString* oldTableName in existsTables) の場合 { NSString* sql = [NSString stringWithFormat:@"存在する場合はテーブルを削除 %@", oldTableName]; [db 実行更新:sql]; } [dbコミット]; }]; MMUserDefault.dbVersion = version_new; } それ以外 { MMUserDefault.dbVersion = version_new; } } - (NSDictionary*)generateMigrationInfosWithOldTables:(NSArray*)oldTables newTables:(NSArray*)newTables { NSMutableDictionary<NSString*, NSArray* >* migrationInfos = [NSMutableDictionary 辞書]; (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]; (publicColumns.count > 0)の場合{ [migrationInfos setObject:publicColumns forKey:newTableName]; } } } 移行情報を返します。 } - (NSArray*)publicColumnsWithOldTableColumns:(NSArray*)oldTableColumns newTableColumns:(NSArray*)newTableColumns { NSMutableArray* publicColumns = [NSMutableArray 配列]; (NSString* oldTableColumn in oldTableColumns) の場合 { if ([newTableColumns containsObject:oldTableColumn]) { [publicColumns addObject:oldTableColumn]; } } publicColumns を返します。 } - (NSArray*)sqliteTableColumnsWithTableName:(NSString*)テーブル名 { __block NSMutableArray<NSString*>* tableColumes = [NSMutableArray 配列]; [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:列名]; } }]; tableColumes を返します。 } - (NSArray*)sqliteExistsTables { __block NSMutableArray<NSString*>* existsTables = [NSMutableArray 配列]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = @"sqlite_master から * を選択し、 type='table' を指定します"; FMResultSet *rs = [db executeQuery:sql]; while ([rs next]) { NSString* テーブル名 = [rs stringForColumn:@"name"]; [existsTables addObject:テーブル名]; } }]; existsTables を返します。 } - (NSArray*)sqlite新しく追加されたテーブル{ __block NSMutableArray<NSString*>* newAddedTables = [NSMutableArray 配列]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = @"sqlite_master から * を選択し、type='table' かつ name が '%_bak' ではない場合"; FMResultSet *rs = [db executeQuery:sql]; while ([rs next]) { NSString* テーブル名 = [rs stringForColumn:@"name"]; [newAddedTables addObject:テーブル名]; } }]; newAddedTablesを返します。 } 質問 SQLite がサイズを変更せずにテーブルファイルを削除する問題 ご質問がありましたら、メッセージを残すか、コミュニティに参加して話し合いましょう。お読みいただきありがとうございます。お役に立てれば幸いです。このサイトをサポートしていただき、ありがとうございます。 以下もご興味があるかもしれません:
|
>>: WeChatミニプログラムページ間の価値転送を実装する方法の例
Magento を頻繁に変更する場合、element.style に遭遇することがあります。 これは...
レスポンシブ レイアウト システムは、今日の一般的な CSS フレームワークではすでに非常に一般的で...
1. 機能: 親コンポーネントが子コンポーネントの指定された位置に HTML 構造を挿入できるように...
<スタイル タイプ="text/css">コードをコピーコードは次の...
この記事の例では、参考までに簡単なカウントダウンを実装するためのjsの具体的なコードを共有しています...
目次概要予防1. 使用方法2. 実装手順予備実装コード効果: Geo共通設定上記の構成を追加した後の...
この記事の例では、カレンダーウィジェットを実装するためのjsの具体的なコードを参考までに共有していま...
目次1. 組み込みオブジェクト2. 数学オブジェクト1. Mathオブジェクトの使用2. 指定された...
シャドウスタイルにおけるフラッターとCSSの対応UIによって指定されたCSSスタイル 幅: 75px...
エラーメッセージ:エラー 1862 (HY000): パスワードの有効期限が切れています。ログインす...
IE8.0の正式版をインストールしたので、基本的なCSS HACKをいくつかまとめてみました。We...
まず、Alibaba Cloud の公式チュートリアルをご覧ください。ファイルの説明: 1. 証明書...
Web デザイナーの頭の中には、仕事に関連する多くの知識が詰まっている必要があります。 CSS は、...
フレックス レイアウトは、エラスティック レイアウトとも呼ばれます。任意のコンテナーをフレックス レ...
CSS のモジュール ソリューションは、JS のモジュール ソリューションと同じくらい多く存在すると...