資料表支援結構演進,允許隨資料需求變化調整資料表結構。 支援下列類型的變更:
- 在任意位置新增欄位
- 重新排序現有的數據行
- 重新命名現有的欄位
- 拓寬現有欄位的型別,請參見 帶有自動結構演化的 Widen 類型
這些變更可以顯性地使用 DDL 或隱性地使用 DML。
Important
結構更新會與所有並發的寫入操作發生衝突。 Databricks 建議協調結構變更以避免寫入衝突。
更新資料表結構會終止從該資料表讀取的任何串流。 若要繼續處理,請使用結構 化串流生產考量中描述的方法重新啟動串流。
手動結構變更
使用 ALTER TABLE 語句明確更改資料表的結構,而不寫入新資料。
新增欄
可為 ALTER TABLE ... ADD COLUMNS 現有表格新增一個或多個欄位,並可選擇指定位置與註解:
ALTER TABLE table_name ADD COLUMNS (col_name data_type [COMMENT col_comment] [FIRST|AFTER colA_name], ...)
根據預設,可空性為 true。
範例:新增巢狀欄位
如需新增欄至巢狀欄位,請使用:
ALTER TABLE table_name ADD COLUMNS (col_name.nested_col_name data_type [COMMENT col_comment] [FIRST|AFTER colA_name], ...)
舉例來說,如果你有一個包含以下結構的表格:
- root
| - colA
| - colB
| +-field1
| +-field2
執行 ALTER TABLE boxes ADD COLUMNS (colB.nested STRING AFTER field1)後,結構會改變為:
- root
| - colA
| - colB
| +-field1
| +-nested
| +-field2
若要在巢狀於 ARRAY 或 MAP 內的結構中新增欄位,請在資料行路徑中使用下列其中一個特殊屬性:
-
element對於 的元素ARRAY -
key用於一個MAP的調 -
value用於MAP的值
例如,下列敘述會根據其型別,在巢狀於 z 內的結構中各新增一個 points 欄位:
-- points is ARRAY<STRUCT<x: DOUBLE, y: DOUBLE>>
ALTER TABLE my_table ADD COLUMNS (points.element.z DOUBLE);
-- points is MAP<STRUCT<x: DOUBLE, y: DOUBLE>, BIGINT>
ALTER TABLE my_table ADD COLUMNS (points.key.z DOUBLE);
-- points is MAP<STRING, STRUCT<x: DOUBLE, y: DOUBLE>>
ALTER TABLE my_table ADD COLUMNS (points.value.z DOUBLE);
更改欄位評論與排序
用 ALTER TABLE ... ALTER COLUMN 來更新欄位的評論或將其相對於其他欄位重新排序:
ALTER TABLE table_name ALTER [COLUMN] col_name (COMMENT col_comment | FIRST | AFTER colA_name)
範例:變更巢狀欄位
若要變更巢狀欄位中的數據行,請使用:
ALTER TABLE table_name ALTER [COLUMN] col_name.nested_col_name (COMMENT col_comment | FIRST | AFTER colA_name)
舉例來說,如果你有一個包含以下結構的表格:
- root
| - colA
| - colB
| +-field1
| +-field2
執行 ALTER TABLE boxes ALTER COLUMN colB.field2 FIRST後,結構會改變為:
- root
| - colA
| - colB
| +-field2
| +-field1
替換欄位
可使用 ALTER TABLE ... REPLACE COLUMNS 此工具重新定義表格的完整欄位清單,包括在單一操作中新增、移除、重新排序或重新命名欄位:
ALTER TABLE table_name REPLACE COLUMNS (col_name1 col_type1 [COMMENT col_comment1], ...)
範例:替換巢狀欄位
例如,執行下列 DDL 時:
ALTER TABLE boxes REPLACE COLUMNS (colC STRING, colB STRUCT<field2:STRING, nested:STRING, field1:STRING>, colA STRING)
如果你有一個包含這個結構的表格:
- root
| - colA
| - colB
| +-field1
| +-field2
結構會改變為:
- root
| - colC
| - colB
| +-field2
| +-nested
| +-field1
| - colA
重新命名欄
若要重新命名數據行而不重寫任何數據行的現有數據,您必須啟用資料表的數據行對應。 請參閱 使用 Delta Lake 欄位映射重新命名和刪除欄位。
若要重新命名欄位:
ALTER TABLE table_name RENAME COLUMN old_col_name TO new_col_name
範例:重新命名巢狀欄位
若要重新命名巢狀欄位:
ALTER TABLE table_name RENAME COLUMN col_name.old_nested_field TO new_nested_field
例如,當您執行下列命令時:
ALTER TABLE boxes RENAME COLUMN colB.field1 TO field001
如果你有一個包含這個結構的表格:
- root
| - colA
| - colB
| +-field1
| +-field2
結構會改變為:
- root
| - colA
| - colB
| +-field001
| +-field2
請參閱 使用 Delta Lake 欄位映射重新命名和刪除欄位。
刪除欄位
若要在不重寫任何資料檔案的情況下,將欄位移除為僅限元資料的操作,您必須啟用資料表的欄位對應。 請參閱 使用 Delta Lake 欄位映射重新命名和刪除欄位。
若要刪除欄:
ALTER TABLE table_name DROP COLUMN col_name
若要刪除多個欄位:
ALTER TABLE table_name DROP COLUMNS (col_name_1, col_name_2)
變更欄位類型或名稱
您可以藉由重寫資料表來變更資料行的類型或名稱,或卸除數據行。 若要這樣做,請使用 overwriteSchema 選項。
下列範例顯示如何變更資料行的類型:
(spark.read.table(...)
.withColumn("birthDate", col("birthDate").cast("date"))
.write
.mode("overwrite")
.option("overwriteSchema", "true")
.saveAsTable(...)
)
下列範例顯示變更資料列名稱:
(spark.read.table(...)
.withColumnRenamed("dateOfBirth", "birthDate")
.write
.mode("overwrite")
.option("overwriteSchema", "true")
.saveAsTable(...)
)
啟用架構演進
使用 WITH SCHEMA EVOLUTION 或將 mergeSchema 設為 true,根據你想要INSERT或MERGE到現有資料表中的資料結構描述來變更結構描述。
啟用結構演化,請使用以下方法之一:
-
INSERT WITH SCHEMA EVOLUTION -
MERGE WITH SCHEMA EVOLUTION你可以在 SQL 語法中使用WITH SCHEMA EVOLUTION,或在 Azure Databricks API 中使用.withSchemaEvolution()。 -
設定
mergeSchema批次寫入或串流寫入的選項。 個別寫入操作時設定.option("mergeSchema", "true")。 -
設定 Spark 配置(舊版配置): 將整個 SparkSession 中的
spark.databricks.delta.schema.autoMerge.enabled設定為true。
Databricks 建議使用 WITH SCHEMA EVOLUTION 語法或選項 mergeSchema 來啟用每次寫入操作的結構演化,而非設定 Spark 設定。
當你使用選項或語法來在寫入操作中啟用結構演化時,這會優先於 Spark 設定。
啟用寫入的架構演進以新增欄位
啟用 schema 演化時,來源查詢中存在但目標資料表中缺少的欄位會自動加入寫入交易。 請參閱 啟用架構演進。
請考慮下列事項:
- 附加新欄位時,會保留大小寫。
- 新的欄位會添加至資料表架構的結尾。
- 如果其他數據行位於結構中,則會附加至目標數據表中結構結尾。
INSERT 使用 SQL 進行結構描述演進
在 Databricks Runtime 18.1 及以上版本中,請在 WITH SCHEMA EVOLUTION 陳述式中使用 INSERT 子句來啟用結構演進:
INSERT WITH SCHEMA EVOLUTION INTO target_table
SELECT * FROM source_table
如果查詢 source_table 回傳的欄位不存在於目標資料表中,這些欄位會自動加入結構中 target_table 。 現有的列會在新欄位中獲得 NULL 值。
該 WITH SCHEMA EVOLUTION 子句支持 INSERT INTO、 INSERT OVERWRITE和 INSERT INTO ... REPLACE 形式。 目標必須是三角洲湖或阿帕奇冰山桌。 在插入至 Hive 或其他非 Delta 資料表中使用此子句時,會傳回錯誤。
如果你使用 Databricks Runtime 18.0 或更低版本,請改用 mergeSchema 選項來啟用結構描述演進。 請參閱 INSERT使用 DataFrame API 的結構描述演進。
INSERT 並透過 DataFrame API 進行結構演化
下列範例示範搭配批次寫入作業使用 mergeSchema 選項:
Python
(spark.read
.table("source_table")
.write
.option("mergeSchema", "true")
.mode("append")
.saveAsTable("target_table")
)
Scala
spark.read
.table("source_table")
.write
.option("mergeSchema", "true")
.mode("append")
.saveAsTable("target_table")
INSERT 使用 Structured Streaming 的結構描述演進
以下範例示範了使用 mergeSchema 結構化串流自動載入器(Auto Loader)的選項。 請參閱 什麼是自動載入器?。
(spark.readStream
.format("cloudFiles")
.option("cloudFiles.format", "json")
.option("cloudFiles.schemaLocation", "<path-to-schema-location>")
.load("<path-to-source-data>")
.writeStream
.option("mergeSchema", "true")
.option("checkpointLocation", "<path-to-checkpoint>")
.trigger(availableNow=True)
.toTable("table_name")
)
合併時的自動結構演化
對於 MERGE,schema 演化可以讓你解決目標與來源資料表之間的結構不匹配。 它會處理下列兩種情況:
欄位存在於來源表格中,但不存在於目標表格中,且在插入或更新操作的指派中以名稱被指定。 或者,存在
UPDATE SET *或INSERT *動作。該數據行會新增至目標架構,而且其值將會從來源中的對應數據行填入。
這隻適用於合併來源中的數據行名稱和結構完全符合目標指派時。
新資料行必須存在於來源結構描述中。 在 action 子句中指派新的資料行並不等於定義該資料行。
這些範例允許架構演進:
-- The column newcol is present in the source but not in the target. It will be added to the target. UPDATE SET target.newcol = source.newcol -- The field newfield doesn't exist in struct column somestruct of the target. It will be added to that struct column. UPDATE SET target.somestruct.newfield = source.somestruct.newfield -- The column newcol is present in the source but not in the target. -- It will be added to the target. UPDATE SET target.newcol = source.newcol + 1 -- Any columns and nested fields in the source that don't exist in target will be added to the target. UPDATE SET * INSERT *如果資料行
newcol不存在於結構描述中source,這些範例不會觸發結構描述演進:UPDATE SET target.newcol = source.someothercol UPDATE SET target.newcol = source.x + source.y UPDATE SET target.newcol = source.output.newcol目標數據表中存在一個列,但源數據表中不存在該列。
目標架構不會變更。 這些欄位:
保持不變
UPDATE SET *。設定為
NULL。INSERT *如果在行動條款中被指定,仍可能被明確修改。
例如:
UPDATE SET * -- The target columns that are not in the source are left unchanged. INSERT * -- The target columns that are not in the source are set to NULL. UPDATE SET target.onlyintarget = 5 -- The target column is explicitly updated. UPDATE SET target.onlyintarget = source.someothercol -- The target column is explicitly updated from some other source column.
您必須手動啟用自動架構演進。 請參閱 啟用架構演進。
Note
在 Databricks Runtime 11.3 LTS 和更早的版本中,架構演進只能使用 INSERT * 或 UPDATE SET * 動作來進行合併。
在 Databricks Runtime 12.2 LTS 和更新版本中,源表中現有的列和結構欄位可以在插入或更新操作中透過名稱來指定。
Databricks Runtime 13.3 LTS 和更新版本中,您可以使用模式演進搭配巢狀結構,例如 map<int, struct<a: int, b: int>>。
MERGE結構演化則使用 SQL、Python 和 Scala
在 Databricks Runtime 15.4 LTS 及以上版本中,你可以使用 SQL 或資料表 API 在合併語句中指定結構演進:
SQL
MERGE WITH SCHEMA EVOLUTION INTO target
USING source
ON source.key = target.key
WHEN MATCHED THEN
UPDATE SET *
WHEN NOT MATCHED THEN
INSERT *
WHEN NOT MATCHED BY SOURCE THEN
DELETE
Python
from delta.tables import *
(targetTable
.merge(sourceDF, "source.key = target.key")
.withSchemaEvolution()
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.whenNotMatchedBySourceDelete()
.execute()
)
Scala
import io.delta.tables._
targetTable
.merge(sourceDF, "source.key = target.key")
.withSchemaEvolution()
.whenMatched()
.updateAll()
.whenNotMatched()
.insertAll()
.whenNotMatchedBySource()
.delete()
.execute()
結構演化的操作 MERGE 範例
以下是 MERGE 操作在有和沒有架構演進情況下的效果範例。
| Columns | 查詢 (在 SQL 中) | 沒有架構演進的行為 (預設值) | 架構演進的行為 |
|---|---|---|---|
目標資料列: key, value來源資料列: key, value, new_value |
MERGE INTO target_table tUSING source_table sON t.key = s.keyWHEN MATCHED THEN UPDATE SET *WHEN NOT MATCHED THEN INSERT * |
數據表架構保持不變;只會更新/插入數據行keyvalue。 |
資料表架構會變更為 (key, value, new_value)。 現有具有匹配項目的記錄會使用value和new_value在來源中進行更新。 新的資料列會依照架構 (key, value, new_value) 插入。 |
目標資料列: key, old_value來源資料列: key, new_value |
MERGE INTO target_table tUSING source_table sON t.key = s.keyWHEN MATCHED THEN UPDATE SET *WHEN NOT MATCHED THEN INSERT * |
UPDATE 和 INSERT 動作會擲回錯誤,因為目標數據行 old_value 不在來源中。 |
資料表架構會變更為 (key, old_value, new_value)。 現有具有相符項目的記錄會用來源中的 new_value 更新,同時保持 old_value 不變。 新的記錄會插入指定的key、new_value和NULL對應的old_value。 |
目標資料列: key, old_value來源資料列: key, new_value |
MERGE INTO target_table tUSING source_table sON t.key = s.keyWHEN MATCHED THEN UPDATE SET new_value = s.new_value |
UPDATE 擲回錯誤,因為數據行 new_value 不存在於目標數據表中。 |
資料表架構會變更為 (key, old_value, new_value)。 具有相符項目的現有記錄會與來源中的new_value同步更新,同時保持old_value不變,而不相符的記錄則在NULL中輸入new_value。 請參閱附註 (1) 。 |
目標資料列: key, old_value來源資料列: key, new_value |
MERGE INTO target_table tUSING source_table sON t.key = s.keyWHEN NOT MATCHED THEN INSERT (key, new_value) VALUES (s.key, s.new_value) |
INSERT 擲回錯誤,因為數據行 new_value 不存在於目標數據表中。 |
資料表架構會變更為 (key, old_value, new_value)。 新的記錄會插入指定的key、new_value和NULL對應的old_value。 現有記錄 NULL 已輸入,讓 new_value 保持 old_value 不變。 請參閱附註 (1) 。 |
(1) 此行為可在 Databricks Runtime 12.2 LTS 及以上版本中提供;在此狀態下,Databricks 執行時 LTS 11.3 及以下版本出現錯誤。
合併時排除欄位
在 Databricks Runtime 12.2 LTS 和更新版本中,您可以在合併條件中使用 EXCEPT 子句來明確排除數據行。
EXCEPT 關鍵詞的行為會根據是否啟用架構演進而有所不同。
當結構描述演化停用時,EXCEPT 關鍵字會套用至目標資料表中的欄位清單,並允許從 UPDATE 或 INSERT 動作中排除欄位。 被排除的欄位會設定為 null。
啟用架構演進后, EXCEPT 關鍵詞會套用至源數據表中的數據行清單,並允許從架構演進中排除數據行。 如果來源中的新欄位不存在於目標資料表中,且該欄位列在 EXCEPT 子句中,則不會新增至目標綱要。 在目標中已存在的排除的欄位會設定為 null。
搭配 EXCLUDE 的 MERGE 範例
下列範例示範此語法:
| Columns | 查詢 (在 SQL 中) | 沒有架構演進的行為 (預設值) | 架構演進的行為 |
|---|---|---|---|
目標資料列: id, title, last_updated來源資料列: id, title, review, last_updated |
MERGE INTO target tUSING source sON t.id = s.idWHEN MATCHED THEN UPDATE SET last_updated = current_date()WHEN NOT MATCHED THEN INSERT * EXCEPT (last_updated) |
比對的數據列會藉由將 last_updated 字段設定為目前日期來更新。 新的列是使用id和title的值來插入的。 排除的欄位 last_updated 會設定為 null。 欄位 review 會被忽略,因為它不在目標中。 |
比對的數據列會藉由將 last_updated 字段設定為目前日期來更新。 架構會演進以新增欄位 review。 新的資料列會使用所有來源欄位插入,設定為last_updated的null除外。 |
目標資料列: id, title, last_updated來源資料列: id, title, review, internal_count |
MERGE INTO target tUSING source sON t.id = s.idWHEN MATCHED THEN UPDATE SET last_updated = current_date()WHEN NOT MATCHED THEN INSERT * EXCEPT (last_updated, internal_count) |
INSERT 擲回錯誤,因為數據行 internal_count 不存在於目標數據表中。 |
比對的數據列會藉由將 last_updated 字段設定為目前日期來更新。 欄位 review 會新增至目標數據表,但 internal_count 會忽略欄位。 插入的新資料列已設定 last_updated 為 null。 |
啟用 Spark 配置的結構演化(舊有系統)
你可以將 Spark 設定 spark.databricks.delta.schema.autoMerge.enabled 設為 true,讓目前 SparkSession 中的所有寫入操作都能進行結構演化:
Python
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", True)
Scala
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", true)
SQL
SET spark.databricks.delta.schema.autoMerge.enabled=true
Note
Databricks 不建議這種做法用於生產環境。 設定全會話配置可能導致多個操作間的非預期結構變更,並使判斷哪些操作演化結構變得困難。
取而代之的是,為每個寫入操作啟用結構演化:
- 對於
INSERT批次/串流寫入,請使用.option("mergeSchema", "true")或INSERT WITH SCHEMA EVOLUTION - 對於
MERGE陳述,請使用MERGE WITH SCHEMA EVOLUTION
當你使用選項或語法來在寫入操作中啟用結構演化時,這會優先於 Spark 設定。
替換表格結構
預設情況下,覆寫資料表中的資料不會覆寫結構描述。 當你使用 mode("overwrite") 而不使用 replaceWhere 來覆寫資料表時,可能仍會想要覆寫所寫入資料的結構描述。
要替換資料表的結構與分割,請將選項 overwriteSchema 設為 true:
df.write.option("overwriteSchema", "true")
Note
您無法在使用動態分割覆寫時指定 overwriteSchema 為 true 。 請參見 使用 partitionOverwriteMode 覆寫動態分割區(legacy)。