Detailliertes Beispiel für die Datenmigration bei einem IOS-Datenbank-Upgrade Zusammenfassung: Vor langer Zeit bin ich auf ein Referenzszenario für ein Upgrade einer Datenbankversion gestoßen. Damals bestand der Ansatz darin, einfach die alten Datenbankdateien zu löschen und die Datenbank- und Tabellenstruktur neu aufzubauen. Diese gewaltsame Upgrade-Methode würde zum Verlust alter Daten führen. Jetzt scheint dies keine elegante Lösung zu sein. Jetzt verwendet ein neues Projekt die Datenbank erneut und ich muss dieses Problem überdenken. Ich hoffe, dieses Problem auf elegantere Weise lösen zu können. Wir werden in Zukunft auf ähnliche Szenarien stoßen und wir alle wollen es besser machen, nicht wahr? Die ideale Situation ist: Wenn die Datenbank aktualisiert wird, ändern sich die Tabellenstruktur, der Primärschlüssel und die Einschränkungen. Nachdem die neue Tabellenstruktur eingerichtet wurde, werden die Daten automatisch aus der alten Tabelle abgerufen und dieselben Felder werden zugeordnet und migriert. In den meisten Geschäftsszenarien umfassen Upgrades der Datenbankversion nur das Hinzufügen oder Entfernen von Feldern und das Ändern von Primärschlüsseleinschränkungen. Daher basiert die im Folgenden zu implementierende Lösung auch auf den grundlegendsten und am häufigsten verwendeten Geschäftsszenarien. Bei komplexeren Szenarien können Sie diese Basis erweitern, um Ihren Erwartungen gerecht zu werden. Auswahl und Finalisierung Nach meiner Onlinesuche habe ich keine einfache und vollständige Lösung für Datenbank-Upgrade und Datenmigration gefunden. Ich habe einige Ideen gefunden. 1. Alte Daten löschen und Tabelle neu aufbauen Vorteile: Einfach Nachteile: Datenverlust 2. Ändern Sie die Tabellenstruktur basierend auf der vorhandenen Tabelle Vorteile: Möglichkeit, Daten beizubehalten. Nachteile: Die Regeln sind relativ kompliziert. Sie müssen eine Datenbankfeldkonfigurationsdatei erstellen, dann die Konfigurationsdatei lesen und SQL ausführen, um die Tabellenstruktur, Einschränkungen, Primärschlüssel usw. zu ändern. Das Aktualisieren der Datenbank über mehrere Versionen hinweg wird mühsam und mühsam. 3. Erstellen Sie eine temporäre Tabelle, kopieren Sie die alten Daten in die temporäre Tabelle, löschen Sie dann die alte Datentabelle und legen Sie die temporäre Tabelle als Datentabelle fest. Vorteile: Möglichkeit zur Datenspeicherung, Unterstützung von Änderungen der Tabellenstruktur, Änderungen an Einschränkungen und Primärschlüsseln, relativ einfache Implementierung Nachteile: erfordert viele Schritte zur Implementierung Unter Berücksichtigung aller Faktoren ist die dritte Methode die zuverlässigere Lösung. Hauptschritte Basierend auf dieser Idee werden die wichtigsten Schritte der Datenbankaktualisierung wie folgt analysiert:
Analyse der verwendeten SQL-Anweisungen Diese Operationen beziehen sich alle auf Datenbankoperationen. Der Schlüssel zum Problem sind also die SQL-Anweisungen der entsprechenden Schritte. Im Folgenden finden Sie eine Analyse der wichtigsten verwendeten SQL-Anweisungen: Holen Sie sich die alte Tabelle in der Datenbank SELECT * von sqlite_master WHERE Typ = "Tabelle" Das Ergebnis ist wie folgt. Sie können sehen, dass es Datenbankfelder wie Typ | Name | tbl_name | rootpage | sql gibt. Wir müssen nur das Namensfeld verwenden, das den Datenbanknamen darstellt. sqlite> SELECT * from sqlite_master WHERE Typ='Tabelle' ...> ; +----------+------------------+-----------+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Typ | Name | Tabellenname | Stammseite | SQL | +----------+------------------+-----------+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Tabelle | t_message_bak | t_message_bak | 2 | TABELLE "t_message_bak" ERSTELLEN (Nachrichten-ID TEXT, Nachrichtentyp INTEGER, Nachrichten-Json-Inhalt TEXT, Zeitzeichenfolge abrufen INTEGER, Zeitzeichenfolge posten INTEGER, Status lesen INTEGER, PRIMÄRSCHLÜSSEL(Nachrichten-ID)) | | Tabelle | t_Nachricht | t_Nachricht | 4 | TABELLE ERSTELLEN t_Nachricht ( Nachrichten-ID TEXT, Nachrichtentyp INTEGER, messageJsonContent TEXT, retriveTimeString INTEGER, postTimeString INTEGER, Lesestatus INTEGER, Spalte hinzufügen INTEGER, PRIMARY KEY(Nachrichten-ID) ) | +----------+------------------+-----------+----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 2 Zeilen im Datensatz (0,03 Sekunden) Ändern Sie den Tabellennamen, fügen Sie das Suffix „_bak“ hinzu und verwenden Sie die alte Tabelle als Sicherungstabelle -- Ändert die Tabelle t_message in die Tabelle t_message_bak ALTER TABLE t_message RENAME TO t_message_bak Abrufen von Tabellenfeldinformationen -- Holen Sie sich die Feldinformationen der Tabelle t_message_bak PRAGMA table_info('t_message_bak') Die erhaltenen Tabellenfeldinformationen lauten wie folgt. Sie können sehen: | cid | name | type | notnull | dflt_value | pk | Bei diesen Datenbankfeldern müssen wir nur das Namensfeld verwenden, das den Feldnamen darstellt. sqlite> PRAGMA table_info('t_message_bak'); +------+--------------------+---------+---------+------------+------+ | cid | Name | Typ | nicht null | dflt_value | pk | +------+--------------------+---------+---------+------------+------+ | 0 | Nachrichten-ID | TEXT | 0 | NULL | 1 | | 1 | Nachrichtentyp | INTEGER | 0 | NULL | 0 | | 2 | messageJsonContent | TEXT | 0 | NULL | 0 | | 3 | retriveTimeString | INTEGER | 0 | NULL | 0 | | 4 | postTimeString | INTEGER | 0 | NULL | 0 | | 5 | Lesestatus | INTEGER | 0 | NULL | 0 | +------+--------------------+---------+---------+------------+------+ 6 Zeilen im Datensatz (0,01 Sekunden) Verwenden von Unterabfragen zur Datenmigration INSERT INTO t_message(Nachrichten-ID, Nachrichtentyp, Nachrichten-JSON-Inhalt, Zeitzeichenfolge abrufen, postTimeString, readState) SELECT Nachrichten-ID, Nachrichtentyp, Nachrichten-Json-Inhalt, retriveTimeString, postTimeString, readState VON t_message_bak Kopieren Sie die Werte der Felder messageID, messageType, messageJsonContent, retriveTimeString, postTimeString und readState in der Tabelle t_message_bak in die Tabelle t_message Code-Implementierung Als nächstes folgt der Schritt der Codeimplementierung. // Eine neue temporäre Tabelle erstellen, die Daten in die temporäre Tabelle importieren und dann die Originaltabelle durch die temporäre Tabelle ersetzen - (void)baseDBVersionControl { NSString * version_alt = ValueOrEmpty(MMUserDefault.dbVersion); NSString * version_new = [NSString stringWithFormat:@"%@", DB_Version]; NSLog(@"dbVersionControl vor: %@ nach: %@",version_alt,version_neu); // Upgrade der Datenbankversion if (version_old != nil && ![version_new isEqualToString:version_old]) { // Holen Sie die alten Tabellen in der Datenbank NSArray* existsTables = [self sqliteExistsTables]; NSMutableArray* tmpExistsTables = [NSMutableArray-Array]; // Ändern Sie den Tabellennamen, fügen Sie das Suffix „_bak“ hinzu und verwenden Sie die alte Tabelle als Sicherungstabelle für (NSString* tablename in existsTables) { [tmpExistsTables addObject:[NSString stringWithFormat:@"%@_bak", Tabellenname]]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = [NSString stringWithFormat:@"ALTER TABLE %@ RENAME TO %@_bak", Tabellenname, Tabellenname]; [db ausführenUpdate:sql]; }]; } existsTables = tmpExistsTables; // Eine neue Tabelle erstellen [self initTables]; // Holen Sie die neu erstellte Tabelle NSArray* newAddedTables = [self sqliteNewAddedTables]; // Die alte und die neue Tabelle durchlaufen, die Felder der Tabelle vergleichen und extrahieren, die migriert werden müssen NSDictionary* migrationInfos = [self generateMigrationInfosWithOldTables:existsTables newTables:newAddedTables]; // Datenmigration wird verarbeitet [migrationInfos enumerateKeysAndObjectsUsingBlock:^(NSString* newTableName, NSArray* publicColumns, BOOL * _Nonnull stop) { NSMutableString* colunmsString = [NSMutableString neu]; für (int i = 0; i<publicColumns.count; i++) { [SpaltenString appendString:öffentlicheSpalten[i]]; wenn (i != publicColumns.count-1) { [Spaltenzeichenfolge Anfügezeichenfolge:@", "]; } } NSMutableString* sql = [NSMutableString neu]; [sql anhängenString:@"INSERT INTO "]; [sql appendString:neuerTabellenname]; [sql anhängenString:@"("]; [sql anhängenString:SpaltenString]; [sql anhängenString:@")"]; [sql Anfügezeichenfolge:@" AUSWÄHLEN "]; [sql anhängenString:SpaltenString]; [sql anhängenString:@" FROM "]; [sql appendFormat:@"%@_bak", neuerTabellenname]; [self.databaseQueue inDatabase:^(FMDatabase *db) { [db ausführenUpdate:sql]; }]; }]; // Lösche die Backup-Tabelle [self.databaseQueue inDatabase:^(FMDatabase *db) { [db beginTransaktion]; für (NSString* alterTabellenname in existierTabellen) { NSString* sql = [NSString stringWithFormat:@"DROP TABLE IF EXISTS %@", alterTabellenname]; [db ausführenUpdate:sql]; } [Datenbank-Commit]; }]; MMUserDefault.dbVersion = version_new; } anders { MMUserDefault.dbVersion = version_new; } } - (NSDictionary*)generateMigrationInfosWithOldTables:(NSArray*)oldTables newTables:(NSArray*)newTables { NSMutableDictionary<NSString*, NSArray* >* migrationInfos = [NSMutableDictionary-Wörterbuch]; für (NSString* neuerTabellenname in neueTabellen) { NSString* alterTabellenname = [NSString stringWithFormat:@"%@_bak", neuerTabellenname]; if ([alteTabellen enthältObjekt:alterTabellenname]) { //Feldinformationen der Tabellendatenbank abrufen NSArray* oldTableColumns = [self sqliteTableColumnsWithTableName:oldTableName]; NSArray* neueTabellenspalten = [self sqliteTableColumnsWithTableName:neuerTabellenname]; NSArray* publicColumns = [self publicColumnsWithOldTableColumns:alteTableColumns newTableColumns:neueTableColumns]; wenn (publicColumns.count > 0) { [migrationInfos setObject:publicColumns fürKey:newTableName]; } } } Migrationsinformationen zurückgeben; } - (NSArray*)publicColumnsWithOldTableColumns:(NSArray*)oldTableColumns newTableColumns:(NSArray*)newTableColumns { NSMutableArray* publicColumns = [NSMutableArray-Array]; für (NSString* alteTabellenspalte in alteTabellenspalten) { if ([neueTabellenspalten enthältObjekt:alteTabellenspalte]) { [publicColumns addObject:alteTabellenspalte]; } } öffentliche Spalten zurückgeben; } - (NSArray*)sqliteTableColumnsWithTableName:(NSString*)tableName { __block NSMutableArray<NSString*>* tableColumes = [NSMutableArray-Array]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = [NSString stringWithFormat:@"PRAGMA table_info('%@')", Tabellenname]; FMResultSet *rs = [db ausführenAbfrage:sql]; während ([rs weiter]) { NSString* Spaltenname = [rs Zeichenfolge für Spalte:@"Name"]; [Tabellenspalten addObject:Spaltenname]; } }]; gibt Tabellenspalten zurück; } - (NSArray*)sqliteExistsTables { __block NSMutableArray<NSString*>* existsTables = [NSMutableArray-Array]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = @"SELECT * from sqlite_master WHERE Typ='Tabelle'"; FMResultSet *rs = [db ausführenAbfrage:sql]; während ([rs weiter]) { NSString* Tabellenname = [rs stringForColumn:@"name"]; [existsTables addObject:Tabellenname]; } }]; gibt existiereTabellen zurück; } - (NSArray*)sqliteNewAddedTables { __block NSMutableArray<NSString*>* newAddedTables = [NSMutableArray-Array]; [self.databaseQueue inDatabase:^(FMDatabase *db) { NSString* sql = @"SELECT * from sqlite_master WHERE Typ='Tabelle' UND Name NICHT WIE '%_bak'"; FMResultSet *rs = [db ausführenAbfrage:sql]; während ([rs weiter]) { NSString* Tabellenname = [rs stringForColumn:@"name"]; [newAddedTables addObject:Tabellenname]; } }]; gibt neue hinzugefügte Tabellen zurück; } Frage Das Problem, dass SQLite Tabellendateien löscht, ohne ihre Größe zu ändern Wenn Sie Fragen haben, hinterlassen Sie bitte eine Nachricht oder kommen Sie zur Diskussion in die Community. Vielen Dank fürs Lesen und ich hoffe, es kann Ihnen helfen. Vielen Dank für Ihre Unterstützung dieser Site! Das könnte Sie auch interessieren:
|
<<: Anpassungsmethode des Linux-Peripheriedateisystems
>>: Beispiel für die Implementierung der Wertübertragung zwischen WeChat-Miniprogrammseiten
Im Internet und in vielen Büchern gibt es viele T...
Als ich mich kürzlich lokal unter Linux anmeldete...
Detaillierte Beschreibung der Verwendung des Medi...
Bei der Verwendung von HTML-Tabellen müssen wir m...
Die Installation komprimierter Pakete hat sich se...
<br /> CSS-Syntax für Tabellenränder Zu den ...
Vorwort Mit der Entwicklung großer Frontends tauc...
Einführung in die Umgebung: Ubuntu Server 16.04.2...
Laden Sie zunächst die grüne kostenlose Installat...
Debug-Zweig Während der normalen Entwicklung eine...
Vorwort: Docker ist eine Open-Source-Anwendungsco...
MySQL hatte zuvor einen Abfragecache, Query Cache...
Durch das Hinzufügen des Attributs rel="nofo...
1. Überprüfen Sie die MySQL-Datenbankkodierung my...
Der Windows Server 2008-Server wird automatisch n...