小巧、快速、可靠。
選擇其中三項。
修改表格 (ALTER TABLE)

1. 概述

alter-table-stmt

ALTER TABLE schema-name . table-name RENAME TO new-table-name RENAME COLUMN column-name TO new-column-name ADD COLUMN column-def DROP COLUMN column-name

欄位定義 (column-def)

SQLite 支援 ALTER TABLE 的功能有限。SQLite 中的 ALTER TABLE 命令允許對現有表格進行以下修改:重新命名表格;重新命名欄位;新增欄位;或刪除欄位。

2. 重新命名表格 (ALTER TABLE RENAME)

RENAME TO 語法將 table-name 的名稱更改為 new-table-name。此命令不能用於在附加的資料庫之間移動表格,只能用於重新命名同一個資料庫中的表格。如果被重新命名的表格具有觸發器或索引,則這些觸發器或索引在表格重新命名後仍會附加到表格。

相容性注意事項:為了將重新命名操作套用到參考已重新命名表格的觸發器和視圖中,版本 3.25.0 (2018-09-15) 和 3.26.0 (2018-12-01) 中增強了 ALTER TABLE 重新命名表格的行為。這被視為一項改進。依賴舊版(且可說是錯誤)行為的應用程式可以使用 PRAGMA legacy_alter_table=ON 陳述式或在 sqlite3_db_config() 介面上使用 SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 設定參數,使 ALTER TABLE RENAME 的行為與 3.25.0 版之前的行為相同。

從 3.25.0 版 (2018-09-15) 開始,觸發器主體和視圖定義中對表格的參考也會被重新命名。

在 3.26.0 版 (2018-12-01) 之前,只有在 PRAGMA foreign_keys=ON 的情況下,或者換句話說,在強制執行 外鍵約束 的情況下,才會編輯對重新命名表格的 FOREIGN KEY 參考。如果 PRAGMA foreign_keys=OFF,則當外鍵所參考的表格(「父表格」)被重新命名時,FOREIGN KEY 約束不會被更改。從 3.26.0 版開始,除非啟用 PRAGMA legacy_alter_table=ON 設定,否則在重新命名表格時,FOREIGN KEY 約束始終會被轉換。下表總結了其中的差異。

PRAGMA foreign_keysPRAGMA legacy_alter_table更新父表格參考SQLite 版本
關閉 (Off)關閉 (Off)< 3.26.0
關閉 (Off)關閉 (Off)>= 3.26.0
開啟 (On)關閉 (Off)所有版本
關閉 (Off)開啟 (On)所有版本
開啟 (On)開啟 (On)所有版本

3. 重新命名欄位 (ALTER TABLE RENAME COLUMN)

RENAME COLUMN TO 語法將表格 table-namecolumn-name 更改為 new-column-name。欄位名稱在表格定義本身以及參考該欄位的所有索引、觸發器和視圖中都會被更改。如果更改欄位名稱會導致觸發器或視圖中的語義歧義,則 RENAME COLUMN 會失敗並顯示錯誤,且不會套用任何更改。

4. 新增欄位 (ALTER TABLE ADD COLUMN)

ADD COLUMN 語法用於向現有表格新增新的欄位。新的欄位永遠附加到現有欄位列表的末尾。column-def 規則定義了新欄位的特性。新欄位可以採用任何在 CREATE TABLE 陳述式中允許的形式,但有以下限制:

新增帶有 CHECK 限制式 的欄位,或在 生成的欄位 上新增 NOT NULL 限制式時,新增的限制式會針對表格中所有先前存在的列進行測試,如果任何限制式失敗,則 ADD COLUMN 會失敗。針對先前存在的列測試新增的限制式是 SQLite 3.37.0 版 (2021-11-27) 的新增強功能。

ALTER TABLE 命令透過修改儲存在 sqlite_schema 表格 中的結構描述 SQL 文字來運作。對於重新命名或新增沒有限制式的欄位,表格內容不會進行任何更改。因此,這類 ALTER TABLE 命令的執行時間與表格中的資料量無關,這類命令在具有 1000 萬列的表格上與在具有 1 列的表格上運行速度一樣快。當新增具有 CHECK 限制式的新欄位,或新增具有 NOT NULL 限制式的生成欄位,或刪除欄位時,則必須讀取(以針對現有列測試新限制式)或寫入(以移除已刪除的欄位)表格中所有現有資料。在這些情況下,ALTER TABLE 命令所需的時間與正在修改的表格中內容的數量成正比。

在資料庫上運行 ADD COLUMN 之後,SQLite 3.1.3 版 (2005-02-20) 和更早版本將無法讀取該資料庫。

5. ALTER TABLE DROP COLUMN

DROP COLUMN 語法用於從表格中移除現有欄位。DROP COLUMN 命令會從表格中移除指定的欄位,並重寫其內容以清除與該欄位相關聯的資料。只有當該欄位沒有被結構描述的任何其他部分參考,並且不是 PRIMARY KEY 且沒有 UNIQUE 限制式時,DROP COLUMN 命令才會生效。DROP COLUMN 命令可能失敗的原因包括:

5.1. 運作方式

SQLite 將結構描述 (schema) 以純文字形式儲存在 sqlite_schema 表格 中。DROP COLUMN 命令(以及所有其他 ALTER TABLE 的變體)會修改該文字,然後嘗試重新解析整個結構描述。只有在修改文字後結構描述仍然有效的情況下,命令才會成功。在 DROP COLUMN 命令的情況下,唯一修改的文字是從 CREATE TABLE 陳述式中移除欄位定義。如果結構描述的其他部分中存在任何欄位的蹤跡,導致修改 CREATE TABLE 陳述式後結構描述無法解析,則 DROP COLUMN 命令將會失敗。

6. 使用 PRAGMA writable_schema=ON 關閉錯誤檢查

如果 ALTER TABLE 遇到 sqlite_schema 表格 中任何無法解析的項目,通常會失敗且不做任何更改。例如,如果有名為「tbl1」的表格關聯了格式錯誤的 VIEW 或 TRIGGER,則嘗試將「tbl1」重新命名為「tbl1neo」將會失敗,因為關聯的 VIEW 和 TRIGGER 無法解析。

從 SQLite 3.38.0 (2022-02-22) 開始,可以透過設定「PRAGMA writable_schema=ON;」來關閉此錯誤檢查。當結構描述可寫入時,ALTER TABLE 會靜默忽略 sqlite_schema 表格中任何無法解析的列。

7. 進行其他類型的表格結構描述變更

SQLite 直接支援的結構描述修改命令僅有上述的「重新命名表格」、「重新命名欄位」、「新增欄位」和「移除欄位」命令。然而,應用程式可以使用一系列簡單的操作對表格的格式進行其他任意更改。對某些表格 X 的結構描述設計進行任意更改的步驟如下:

  1. 如果啟用了外鍵約束,請使用 PRAGMA foreign_keys=OFF 將其關閉。

  2. 開始一個交易。

  3. 記住與表格 X 關聯的所有索引、觸發器和視圖的格式。此資訊將在下面的步驟 8 中需要。一種方法是執行如下查詢:SELECT type, sql FROM sqlite_schema WHERE tbl_name='X'。

  4. 使用 CREATE TABLE 建立一個名為「new_X」的新表格,該表格採用表格 X 的所需修改格式。當然,請確保名稱「new_X」不會與任何現有的表格名稱衝突。

  5. 使用以下陳述式將內容從 X 傳輸到 new_X:INSERT INTO new_X SELECT ... FROM X。

  6. 刪除舊表格 X:DROP TABLE X

  7. 使用以下命令將 new_X 的名稱更改為 X:ALTER TABLE new_X RENAME TO X。

  8. 使用 CREATE INDEXCREATE TRIGGERCREATE VIEW 重建與表格 X 關聯的索引、觸發器和視圖。可以參考步驟 8 中儲存的觸發器、索引和視圖的舊格式作為指南,並根據修改進行適當的更改。

  9. 如果任何視圖以受結構描述變更影響的方式參考表格 X,則使用 DROP VIEW 刪除這些視圖,並使用 CREATE VIEW 重新建立它們,並進行必要的更改以適應結構描述變更。

  10. 如果原本啟用了外鍵約束,請執行 PRAGMA foreign_key_check 以驗證結構描述變更沒有破壞任何外鍵約束。

  11. 提交在步驟 7 中開始的交易。

  12. 如果原本啟用了外鍵約束,請立即重新啟用它們。

注意:請務必嚴格遵循上述程序。以下兩個方塊總結了修改表格定義的兩種程序。乍看之下,它們似乎都能達到同樣的目的。然而,右側的程序並不總是有效,尤其是在版本 3.25.0 和 3.26.0 新增的強化版 重新命名表格 功能之後。在右側的程序中,將表格初始重新命名為臨時名稱可能會損壞觸發器、視圖和外鍵約束中對該表格的引用。左側的安全程序使用新的臨時名稱構建修改後的表格定義,然後將表格重新命名為最終名稱,這樣不會破壞連結。

  1. 建立新表格
  2. 複製資料
  3. 刪除舊表格
  4. 將新表格重新命名為舊表格的名稱
  1. 重新命名舊表格
  2. 建立新表格
  3. 複製資料
  4. 刪除舊表格

正確

錯誤

即使結構變更導致表格中儲存的資訊發生變化,上述 12 步驟的 通用 ALTER TABLE 程序 仍然有效。因此,上述完整的 12 步驟程序適用於刪除欄位、更改欄位順序、新增或移除 UNIQUE 約束或 PRIMARY KEY、新增 CHECK 或 FOREIGN KEY 或 NOT NULL 約束,或更改欄位資料類型等情況。然而,對於某些不影響磁碟上內容的變更,可以選擇使用更簡單、更快速的程序。以下較簡單的程序適用於移除 CHECK 或 FOREIGN KEY 或 NOT NULL 約束,或新增、移除或更改欄位的預設值。

  1. 開始一個交易。

  2. 執行 PRAGMA schema_version 以確定目前的結構版本號碼。此號碼將在以下步驟 6 中使用。

  3. 使用 PRAGMA writable_schema=ON 啟用結構編輯。

  4. 執行 UPDATE 陳述式來更改 sqlite_schema 表格 中表格 X 的定義:UPDATE sqlite_schema SET sql=... WHERE type='table' AND name='X';

    注意:如果對 sqlite_schema 表格進行這樣的更改包含語法錯誤,將會導致資料庫損毀且無法讀取。建議在使用此陳述式處理包含重要資料的資料庫之前,先在單獨的空白資料庫上仔細測試 UPDATE 陳述式。

  5. 如果對表格 X 的更改也會影響結構中的其他表格、索引、觸發器或視圖,則也需執行 UPDATE 陳述式來修改這些其他表格、索引和視圖。例如,如果欄位名稱發生更改,則必須修改所有引用該欄位的 FOREIGN KEY 約束、觸發器、索引和視圖。

    注意:再次提醒,如果對 sqlite_schema 表格進行這樣的更改包含錯誤,將會導致資料庫損毀且無法讀取。在使用此程序處理包含重要資料的資料庫之前,請先在單獨的測試資料庫上仔細測試整個程序,並/或在執行此程序之前備份重要的資料庫。

  6. 使用 PRAGMA schema_version=X 增加結構版本號碼,其中 X 比步驟 2 中找到的舊結構版本號碼大 1。

  7. 使用 PRAGMA writable_schema=OFF 關閉結構編輯。

  8. (選用)執行 PRAGMA integrity_check 以驗證結構變更沒有損壞資料庫。

  9. 提交在步驟 1 中開始的事務。

如果 SQLite 的未來版本新增了新的 ALTER TABLE 功能,這些功能很可能會使用上述兩種程序之一。

8. 為何 ALTER TABLE 對 SQLite 來說是個問題

大多數 SQL 資料庫引擎將已解析的結構儲存在各種系統表格中。在這些資料庫引擎上,ALTER TABLE 僅需修改對應的系統表格即可。

SQLite 的不同之處在於它將結構描述儲存在 sqlite_schema 表格中,以定義結構描述的 CREATE 陳述式原始文字形式儲存。因此,ALTER TABLE 需要修改 CREATE 陳述式的文字。對於某些「具創意」的結構描述設計,這樣做可能會很棘手。

以文字形式儲存結構描述的 SQLite 方法對於嵌入式關聯式資料庫具有優勢。首先,這表示結構描述在資料庫檔案中佔用的空間較少。這一點很重要,因為常見的 SQLite 使用模式是擁有許多小型、獨立的資料庫檔案,而不是將所有內容都放在一個大型的全域資料庫檔案中,這是用戶端/伺服器資料庫引擎的常用方法。由於結構描述在每個獨立的資料庫檔案中都會重複,因此保持結構描述表示法的精簡非常重要。

以文字而非剖析後的表格儲存結構描述也賦予了實作的彈性。由於每次開啟資料庫時都會重新產生結構描述的內部剖析,因此結構描述的內部表示法可以從一個版本變更到另一個版本。這一點很重要,因為有時新功能需要增強內部結構描述表示法。如果結構描述表示法在資料庫檔案中公開,則更改內部結構描述表示法將會更加困難。換句話說,以文字形式儲存結構描述有助於維持回溯相容性,並有助於確保較新版本的 SQLite 可以讀取和寫入較舊的資料庫檔案。

以文字形式儲存結構描述也讓 SQLite 資料庫檔案格式 更易於定義、記錄和理解。這有助於使 SQLite 資料庫檔案成為 建議的長期資料封存儲存格式

以文字形式儲存結構描述的缺點是可能會讓修改結構描述變得棘手。因此,SQLite 中 ALTER TABLE 的支援在傳統上落後於其他 SQL 資料庫引擎,這些引擎將其結構描述儲存為剖析後的系統表格,更容易修改。

本頁面最後修改時間為世界協調時間 2022-08-10 18:45:48