답변:
정기적으로 sqlite 데이터베이스를 업데이트하고 이전 데이터베이스를 새 스키마로 마이그레이션해야하는 응용 프로그램을 유지 관리하며 다음 작업을 수행합니다.
데이터베이스 버전을 추적하기 위해 sqlite가 제공하는 내장 된 사용자 버전 변수를 사용합니다 (sqlite는이 변수에 대해 아무 작업도하지 않습니다. 원하는대로 자유롭게 사용할 수 있습니다). 0에서 시작하며 다음 sqlite 문을 사용하여이 변수를 가져 오거나 설정할 수 있습니다.
> PRAGMA user_version;
> PRAGMA user_version = 1;
앱이 시작되면 현재 사용자 버전을 확인하고 스키마를 최신 상태로 유지하는 데 필요한 변경 사항을 적용한 다음 사용자 버전을 업데이트합니다. 문제가 발생하더라도 변경 사항이 커밋되지 않도록 트랜잭션에 업데이트를 래핑합니다.
스키마 변경을 위해 sqlite는 특정 작업 (테이블 이름 변경 또는 열 추가)에 대해 "ALTER TABLE"구문을 지원합니다. 이는 기존 테이블을 제자리에서 쉽게 업데이트 할 수있는 방법입니다. http://www.sqlite.org/lang_altertable.html 에서 문서를 참조 하십시오 . "ALTER TABLE"구문에서 지원하지 않는 열 또는 기타 변경 사항을 삭제하기 위해 새 테이블을 만들고 날짜를 마이그레이션하고 이전 테이블을 삭제하고 새 테이블의 이름을 원래 이름으로 바꿉니다.
Just Curious의 대답은 정확하지 않습니다. (제 요점이 있습니다!) 현재 앱에있는 데이터베이스 스키마 버전을 추적하는 데 사용합니다.
앱의 예상 스키마 버전과 일치하는 user_version을 가져 오기 위해 발생해야하는 마이그레이션을 실행하기 위해 switch 문을 사용합니다. 다음은 우리 앱에서 이것이 어떻게 보이는지에 대한 잘라낸 예입니다. Strip .
- (void) migrateToSchemaFromVersion:(NSInteger)fromVersion toVersion:(NSInteger)toVersion {
// allow migrations to fall thru switch cases to do a complete run
// start with current version + 1
[self beginTransaction];
switch (fromVersion + 1) {
case 3:
// change pin type to mode 'pin' for keyboard handling changes
// removing types from previous schema
sqlite3_exec(db, "DELETE FROM types;", NULL, NULL, NULL);
NSLog(@"installing current types");
[self loadInitialData];
case 4:
//adds support for recent view tracking
sqlite3_exec(db, "ALTER TABLE entries ADD COLUMN touched_at TEXT;", NULL, NULL, NULL);
case 5:
{
sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN image TEXT;", NULL, NULL, NULL);
sqlite3_exec(db, "ALTER TABLE categories ADD COLUMN entry_count INTEGER;", NULL, NULL, NULL);
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_id_idx ON categories(id);", NULL, NULL, NULL);
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS categories_name_id ON categories(name);", NULL, NULL, NULL);
sqlite3_exec(db, "CREATE INDEX IF NOT EXISTS entries_id_idx ON entries(id);", NULL, NULL, NULL);
// etc...
}
}
[self setSchemaVersion];
[self endTransaction];
}
toVersion
이 당신의 코드에서 어디에 사용하는지 보지 못했 습니까? 버전 0에 있고 그 이후 버전이 두 개 더있을 때 어떻게 처리됩니까? 이것은 0에서 1로, 1에서 2로 마이그레이션해야 함을 의미합니다. 어떻게 처리합니까?
break
문 이 없으므로 switch
모든 후속 마이그레이션도 발생합니다.
FMDB 및 MBProgressHUD와 몇 가지 마이그레이션 코드를 공유하겠습니다.
다음은 스키마 버전 번호를 읽고 쓰는 방법입니다 (아마도 모델 클래스의 일부일 것입니다. 제 경우에는 Database라고하는 싱글 톤 클래스입니다).
- (int)databaseSchemaVersion {
FMResultSet *resultSet = [[self database] executeQuery:@"PRAGMA user_version"];
int version = 0;
if ([resultSet next]) {
version = [resultSet intForColumnIndex:0];
}
return version;
}
- (void)setDatabaseSchemaVersion:(int)version {
// FMDB cannot execute this query because FMDB tries to use prepared statements
sqlite3_exec([self database].sqliteHandle, [[NSString stringWithFormat:@"PRAGMA user_version = %d", DatabaseSchemaVersionLatest] UTF8String], NULL, NULL, NULL);
}
다음 [self database]
은 데이터베이스를 느리게 여는 방법입니다.
- (FMDatabase *)database {
if (!_databaseOpen) {
_databaseOpen = YES;
NSString *documentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *databaseName = [NSString stringWithFormat:@"userdata.sqlite"];
_database = [[FMDatabase alloc] initWithPath:[documentsDir stringByAppendingPathComponent:databaseName]];
_database.logsErrors = YES;
if (![_database openWithFlags:SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FILEPROTECTION_COMPLETE]) {
_database = nil;
} else {
NSLog(@"Database schema version is %d", [self databaseSchemaVersion]);
}
}
return _database;
}
다음은 뷰 컨트롤러에서 호출되는 마이그레이션 메서드입니다.
- (BOOL)databaseNeedsMigration {
return [self databaseSchemaVersion] < databaseSchemaVersionLatest;
}
- (void)migrateDatabase {
int version = [self databaseSchemaVersion];
if (version >= databaseSchemaVersionLatest)
return;
NSLog(@"Migrating database schema from version %d to version %d", version, databaseSchemaVersionLatest);
// ...the actual migration code...
if (version < 1) {
[[self database] executeUpdate:@"CREATE TABLE foo (...)"];
}
[self setDatabaseSchemaVersion:DatabaseSchemaVersionLatest];
NSLog(@"Database schema version after migration is %d", [self databaseSchemaVersion]);
}
다음은 MBProgressHUD를 사용하여 진행률 베젤을 표시하는 마이그레이션을 호출하는 루트보기 컨트롤러 코드입니다.
- (void)viewDidAppear {
[super viewDidAppear];
if ([[Database sharedDatabase] userDatabaseNeedsMigration]) {
MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:self.view.window];
[self.view.window addSubview:hud];
hud.removeFromSuperViewOnHide = YES;
hud.graceTime = 0.2;
hud.minShowTime = 0.5;
hud.labelText = @"Upgrading data";
hud.taskInProgress = YES;
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[hud showAnimated:YES whileExecutingBlock:^{
[[Database sharedDatabase] migrateUserDatabase];
} onQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) completionBlock:^{
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}];
}
}
schema_version
pragma는 일반적으로 사람들이 다루는 것이 아닙니다.
최고의 솔루션 IMO는 SQLite 업그레이드 프레임 워크를 구축하는 것입니다. 나는 (C # 세계에서) 같은 문제가 있었고 나만의 프레임 워크를 만들었습니다. 여기에서 그것에 대해 읽을 수 있습니다 . 그것은 완벽하게 작동하며 최소한의 노력으로 내 (이전 악몽 같은) 업그레이드가 작동하도록 만듭니다.
라이브러리는 C #으로 구현되지만 여기에 제시된 아이디어는 귀하의 경우에도 잘 작동합니다.
1
. /migrations
SQL 기반 마이그레이션 목록이있는 폴더를 만듭니다 . 여기서 각 마이그레이션은 다음과 같습니다.
/migrations/001-categories.sql
-- Up
CREATE TABLE Category (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO Category (id, name) VALUES (1, 'Test');
-- Down
DROP TABLE User;
/migrations/002-posts.sql
-- Up
CREATE TABLE Post (id INTEGER PRIMARY KEY, categoryId INTEGER, text TEXT);
-- Down
DROP TABLE Post;
2
. 적용된 마이그레이션 목록이 포함 된 db 테이블을 만듭니다. 예를 들면 다음과 같습니다.
CREATE TABLE Migration (name TEXT);
3
. 시작하기 전에 애플리케이션 부트 스트랩 로직을 업데이트하여/migrations
폴더 아직 적용되지 않은 마이그레이션을 실행 .
다음은 JavaScript로 구현 된 예제입니다 : Node.js 앱용 SQLite 클라이언트
몇 가지 팁 ...
1) 데이터베이스를 NSOperation으로 마이그레이션하는 모든 코드를 넣고 백그라운드 스레드에서 실행하는 것이 좋습니다. 데이터베이스가 마이그레이션되는 동안 스피너가있는 사용자 정의 UIAlertView를 표시 할 수 있습니다.
2) 번들에서 앱 문서로 데이터베이스를 복사하고 해당 위치에서 사용하는지 확인하십시오. 그렇지 않으면 각 앱 업데이트로 전체 데이터베이스를 덮어 쓴 다음 새 빈 데이터베이스를 마이그레이션합니다.
3) FMDB는 훌륭하지만 executeQuery 메서드는 어떤 이유로 든 PRAGMA 쿼리를 수행 할 수 없습니다. PRAGMA user_version을 사용하여 스키마 버전을 확인하려면 sqlite3를 직접 사용하는 자체 메서드를 작성해야합니다.
4)이 코드 구조는 사용자가 앱 업데이트간에 이동하는 시간에 관계없이 업데이트가 순서대로 실행되고 모든 업데이트가 실행되도록합니다. 추가로 리팩토링 할 수 있지만 이것은 매우 간단한 방법입니다. 이 방법은 데이터 싱글 톤이 인스턴스화 될 때마다 안전하게 실행할 수 있으며 데이터 싱글 톤을 올바르게 설정 한 경우 세션 당 한 번만 발생하는 하나의 작은 db 쿼리 비용 만 발생합니다.
- (void)upgradeDatabaseIfNeeded {
if ([self databaseSchemaVersion] < 3)
{
if ([self databaseSchemaVersion] < 2)
{
if ([self databaseSchemaVersion] < 1)
{
// run statements to upgrade from 0 to 1
}
// run statements to upgrade from 1 to 2
}
// run statements to upgrade from 2 to 3
// and so on...
// set this to the latest version number
[self setDatabaseSchemaVersion:3];
}
}
.net의 경우 lib를 사용할 수 있습니다.
EntityFrameworkCore.Sqlite.Migrations
간단하므로 다른 플랫폼의 경우 lib에서와 동일한 동작을 쉽게 구현할 수 있습니다.