在工做中,因爲新需求老是不斷,咱們常常會遇到項目中的一些表的結構要改變,好比最多見的就是 新增字段 了。這裏我總結一下我遇到這種狀況時的處理方法,SQLite 也有一些坑,但願能幫到有一樣需求的朋友們。下面我是用的 fmdb 進行的數據庫操做,用原生或者其餘工具的升級思想也是同樣的。html
用戶升級 app 時,當咱們判斷到舊項目的版本號(或者數據庫的單獨版本號)小於某個版本號時,就進行咱們的升級操做。python
若是你要新增的是個普通字段,並非個 主鍵(後面會介紹什麼狀況下會新增主鍵),那就好辦了,操做很簡單,直接執行新增字段語句便可。sql
先複習一下 SQL 的 新增字段 語句:
alter table mydownload add column 'IsFree' varchar(100) default '1'
其中的 column 能夠省略,同時也能夠不給 default 默認值。數據庫
下面是判斷升級並進行新增操做的具體代碼:app
if (![dbPointer columnExists:@"LoginUserId" inTableWithName:@"mydownload"]) {
// 若是不存在 LoginUserId 字段則執行添加 LoginUserId 語句,默認值是當前登陸的用戶id
NSString *addStr = [NSString stringWithFormat:@"alter table mydownload add column 'LoginUserId' varchar(100) default '%@'", [Global sharedGlobal].loginInfo.userId];
if ([dbPointer executeUpdate:addStr]){
DebugLog(@"添加 LoginUserId 字段成功!");
} else {
DebugLog(@"添加 LoginUserId 字段失敗!");
}
}
複製代碼
⚠️ 注意: 這裏要說一下,咱們的 SQLite 是個閹割的數據庫,有不少數據庫操做語句都是不支持的,好比不支持 批量增長字段,因此若是你要新增多個字段,那也只能一個一個的加了。工具
若是你新增的是個 主鍵。好比你想在之前的文章 id 爲主鍵的的基礎上,再新增一個用戶 id,把文章 id 和用戶 id 做爲 聯合主鍵,那此時就不能執行新增字段方法了,也是由於 SQLite 的限制,ui
⚠️ 注意: SQLite 限制了 ALTER TABLE 的部分功能,只能將列添加到表的末尾或更改表的名稱。 若是要在表的結構中進行更復雜的更改,則必須 從新建立表。 您能夠將現有數據保存到臨時表,刪除舊錶,建立新表,而後從臨時表中複製數據。spa
例如,假設您有一個名爲「person」的表,其列名爲「id」,「name」和「age」,而且您要今後表中刪除列「age」。 如下 SQL 語句步驟說明了如何完成此操做:.net
BEGIN TRANSACTION; CREATE TEMPORARY TABLE person_backup(id,name); INSERT INTO person_backup SELECT id,name FROM person; DROP TABLE person; CREATE TABLE person(id,name); INSERT INTO person SELECT id,name FROM person_backup; DROP TABLE person_backup; COMMIT; 複製代碼
下面再把我項目中具體的一個簡略例子展現出來供你們參考: 原表狀態: 原下載表 mydownload 只有兩個字段 EpisodeId 和 DownloaderFilePath,主鍵是 EpisodeId 。下載的東西不跟隨用戶。 新需求: 如今要求下載的東西跟隨用戶走,即下載列表只展現當前用戶下載的東西。這就須要新增一個 LoginUserId 用戶 id 字段,把它和 EpisodeId 做爲一個聯合主鍵,這樣下載的東西就能夠和用戶 id 綁定了。 具體代碼操做:code
if (![dbPointer columnExists:@"LoginUserId" inTableWithName:@"mydownload"]) {
// 若是不存在 LoginUserId 字段則將原來的表更名
[dbPointer beginTransaction];
BOOL isRollBack = NO;
@try {
if ([dbPointer executeUpdate:@"ALTER TABLE mydownload RENAME TO temp_mydownload"]) {
NSString *executeStr = @"CREATE TABLE mydownload (EpisodeId varchar(100), LoginUserId varchar(100), DownloaderFilePath varchar(100),CONSTRAINT PK_mydownload PRIMARY KEY(EpisodeId,LoginUserId) )";
if ([dbPointer executeUpdate:executeStr]) {
// 從舊數據表把舊數據插入新的數據表中
NSString *insertSql = [NSString stringWithFormat:@"INSERT INTO mydownload (EpisodeId,LoginUserId,DownloaderFilePath) select EpisodeId,'%@','1','',DownloaderFilePath from temp_mydownload", [Global sharedGlobal].loginInfo.userId];// 複製更名後的表到新建的表(注意:一列對應一列的進行復制,新增的字段能夠用''來補)
if ([dbPointer executeUpdate:insertSql]) {
[dbPointer executeUpdate:@"drop table temp_mydownload"];// 刪除舊錶
[[NSUserDefaults standardUserDefaults] setObject:@"YES" forKey:@"hasModifyDownloadDatabase"];// 標記已經升級過下載表
};
}
}
} @catch (NSException *exception) {
isRollBack = YES;
// 事務回退
[dbPointer rollback];
} @finally {
if (!isRollBack) {
// 事務提交
[dbPointer commit];
}
}
}
複製代碼
以上的總結參考了並部分摘抄瞭如下文章,很是感謝如下做者的分享!:
二、《sqlite並不支持建表後修改主鍵,或刪除列,若是要修改,請參考以下作法》
轉載請備註原文出處,不得用於商業傳播——凡幾多