更新 Delta Lake 資料表結構描述
Delta Lake 可讓您更新資料表的結構描述。 支援下列類型的變更:
- 新增資料列 (在任意位置)
- 重新排序現有的數據行
- 重新命名現有的數據行
您可以明確使用 DDL 或隱含地使用 DML 來進行這些變更。
重要
Delta 數據表架構的更新是與所有並行 Delta 寫入作業衝突的作業。
更新 Delta 表格架構時,從該表格讀取的串流將會終止。 如果您想要讓資料流程繼續,您必須加以重新啟動。 如需建議的方法,請參閱 結構化串流的生產考慮。
明確更新結構描述以新增資料行
ALTER TABLE table_name ADD COLUMNS (col_name data_type [COMMENT col_comment] [FIRST|AFTER colA_name], ...)
根據預設,Null 屬性為 true
。
若要將資料行加入巢狀欄位,請使用:
ALTER TABLE table_name ADD COLUMNS (col_name.nested_col_name data_type [COMMENT col_comment] [FIRST|AFTER colA_name], ...)
例如,如果執行 ALTER TABLE boxes ADD COLUMNS (colB.nested STRING AFTER field1)
前的架構為:
- root
| - colA
| - colB
| +-field1
| +-field2
之後的架構為:
- root
| - colA
| - colB
| +-field1
| +-nested
| +-field2
注意
僅支持結構新增巢狀數據行。 不支援數位和對應。
明確更新結構描述以變更資料行註解或排序
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)
例如,如果執行 ALTER TABLE boxes ALTER COLUMN colB.field2 FIRST
前的架構為:
- root
| - colA
| - colB
| +-field1
| +-field2
之後的架構為:
- root
| - colA
| - colB
| +-field2
| +-field1
明確更新結構描述以取代資料行
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
明確更新架構以重新命名數據行
注意
這項功能適用於 Databricks Runtime 10.4 LTS 和更新版本。
若要重新命名數據行而不重寫任何數據行的現有數據,您必須啟用資料表的數據行對應。 請參閱重新命名和卸除與 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 資料行對應的資料行。
明確更新架構以卸除數據行
注意
這項功能適用於 Databricks Runtime 11.3 LTS 和更新版本。
若要將數據行卸除為僅限元數據的作業,而不需重寫任何數據檔,您必須啟用數據表的數據行對應。 請參閱重新命名和卸除與 Delta Lake 資料行對應的資料行。
重要
從元數據卸除數據行並不會刪除檔案中數據行的基礎數據。 若要清除卸除的數據行數據,您可以使用 REORG TABLE 來重寫檔案。 然後 ,您可以使用 VACUUM 來實際刪除包含已卸載數據行資料的檔案。
若要卸除數據行:
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(...)
)
啟用架構演進
您可以執行下列其中一項來啟用架構演進:
.option("mergeSchema", "true")
將 設定為 Spark DataFramewrite
或writeStream
作業。 請參閱 啟用架構演進以新增數據行的寫入。- 使用
MERGE WITH SCHEMA EVOLUTION
語法。 請參閱<合併的結構描述演進語法>。 - 將 Spark conf
spark.databricks.delta.schema.autoMerge.enabled
設定為true
目前的 SparkSession。
Databricks 建議為每個寫入作業啟用架構演進,而不是設定 Spark conf。
當您使用選項或語法在寫入作業中啟用架構演進時,這會優先於Spark conf。
注意
語句沒有架構演進子句 INSERT INTO
。
啟用寫入的架構演進以新增數據行
啟用架構演進時,來源查詢中存在但目標數據表遺漏的數據行會自動新增為寫入交易的一部分。 請參閱 啟用架構演進。
附加新數據行時,會保留大小寫。 新的數據行會新增至數據表架構的結尾。 如果其他數據行位於結構中,則會附加至目標數據表中結構結尾。
下列範例示範搭配自動載入器使用 mergeSchema
選項。 請參閱 什麼是自動載入器?。
(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")
)
下列範例示範搭配批次寫入作業使用 mergeSchema
選項:
(spark.read
.table(source_table)
.write
.option("mergeSchema", "true")
.mode("append")
.saveAsTable("table_name")
)
Delta Lake merge 的自動架構演進
架構演進可讓使用者解決合併中目標與源數據表之間的架構不符。 它會處理下列兩種情況:
- 源數據表中的數據行不存在於目標數據表中。 新的數據行會新增至目標架構,並使用來源值插入或更新其值。
- 目標數據表中的數據行不存在於源數據表中。 目標架構維持不變;其他目標數據行中的值會維持不變(針對
UPDATE
)或設定為NULL
(forINSERT
)。
您必須手動啟用自動架構演進。 請參閱 啟用架構演進。
注意
在 Databricks Runtime 12.2 LTS 和更新版本中,源數據表中存在的數據行和結構字段可以透過名稱來指定或更新動作。 在 Databricks Runtime 11.3 LTS 和以下版本中,只能 INSERT *
使用 或 UPDATE SET *
動作搭配合併進行架構演進。
Databricks Runtime 13.3 LTS 和更新版本中,您可以使用架構演進搭配結構巢狀結構,例如 map<int, struct<a: int, b: int>>
。
合併的架構演進語法
在 Databricks Runtime 15.2 和更新版本中,您可以使用 SQL 或 Delta 數據表 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
一些範例。
資料行 | 查詢 (在 SQL 中) | 沒有架構演進的行為 (預設值) | 架構演進的行為 |
---|---|---|---|
目標資料列: key, value 來源資料列: key, value, new_value |
MERGE INTO target_table t USING source_table s ON t.key = s.key WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT * |
數據表架構保持不變;只會更新/插入數據行key value 。 |
資料表架構會變更為 (key, value, new_value) 。 具有相符項目的現有記錄會使用 和 value new_value 在來源中更新。 新的資料欄會插入架構 (key, value, new_value) 。 |
目標資料列: key, old_value 來源資料列: key, new_value |
MERGE INTO target_table t USING source_table s ON t.key = s.key WHEN 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 t USING source_table s ON t.key = s.key WHEN 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 t USING source_table s ON t.key = s.key WHEN 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 Runtime 11.3 LTS 和以下錯誤。
使用 Delta Lake merge 排除數據行
在 Databricks Runtime 12.2 LTS 和更新版本中,您可以在合併條件中使用 EXCEPT
子句來明確排除數據行。 關鍵詞的行為 EXCEPT
會根據是否啟用架構演進而有所不同。
停用架構演進時, EXCEPT
關鍵詞會套用至目標數據表中的數據行清單,並允許從 UPDATE
或 INSERT
動作排除數據行。 排除的資料列會設定為 null
。
啟用架構演進后, EXCEPT
關鍵詞會套用至源數據表中的數據行清單,並允許從架構演進中排除數據行。 如果目標列在 子句中 EXCEPT
,則來源中不存在的新數據行不會新增至目標架構。 目標中已存在的排除資料列會設定為 null
。
下列範例示範此語法:
資料行 | 查詢 (在 SQL 中) | 沒有架構演進的行為 (預設值) | 架構演進的行為 |
---|---|---|---|
目標資料列: id, title, last_updated 來源資料列: id, title, review, last_updated |
MERGE INTO target t USING source s ON t.id = s.id WHEN MATCHED THEN UPDATE SET last_updated = current_date() WHEN NOT MATCHED THEN INSERT * EXCEPT (last_updated) |
比對的數據列會藉由將 last_updated 字段設定為目前日期來更新。 新的數據列會使用 和title 的值id 插入。 排除的欄位 last_updated 會設定為 null 。 欄位 review 會被忽略,因為它不在目標中。 |
比對的數據列會藉由將 last_updated 字段設定為目前日期來更新。 架構會演進以新增欄位 review 。 新的數據列會使用所有來源字段插入,但設定為 null 除外last_updated 。 |
目標資料列: id, title, last_updated 來源資料列: id, title, review, internal_count |
MERGE INTO target t USING source s ON t.id = s.id WHEN 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 。 |
NullType
處理架構更新中的數據行
因為 Parquet 不支援 NullType
, NullType
因此當寫入 Delta 數據表時,數據行會從 DataFrame 卸除,但仍會儲存在架構中。 當收到該數據行的不同數據類型時,Delta Lake 會將架構合併到新的數據類型。 如果 Delta Lake 收到 NullType
現有數據行的 ,則會保留舊的架構,並在寫入期間卸除新的數據行。
NullType
不支援串流。 因為您必須在使用串流時設定架構,這應該非常罕見。 NullType
也不接受複雜型別,例如 ArrayType
和 MapType
。
取代資料表結構描述
根據預設,覆寫數據表中的數據不會覆寫架構。 使用 mode("overwrite")
不使用 replaceWhere
覆寫數據表時,您可能仍想要覆寫所寫入數據的架構。 您可以將 選項設定 overwriteSchema
為 true
,以取代資料表的架構和資料分割:
df.write.option("overwriteSchema", "true")
重要
您無法在使用動態分割覆寫時指定 overwriteSchema
為 true
。