테이블은 스키마 진화를 지원하므로 데이터 요구 사항이 변경되면 테이블 구조를 수정할 수 있습니다. 지원되는 변경 유형은 다음과 같습니다.
- 임의의 위치에 새 열 추가
- 기존 열 순서 다시 지정
- 기존 열 이름 바꾸기
- 기존 열을 확대하는 형식, 자동 스키마가 진화된 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], ...)
기본적으로 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 ... 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)
예를 들어 ALTER TABLE boxes ALTER COLUMN colB.field2 FIRST를 실행하기 전의 스키마가 다음과 같은 경우가 있습니다.
- root
| - colA
| - colB
| +-field1
| +-field2
실행 이후 스키마는 다음과 같습니다.
- 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 열 매핑을 사용하여 열 이름 바꾸기 및 삭제를 참조하세요.
Note
메타데이터에서 열을 삭제해도 파일의 열에 대한 기본 데이터는 삭제되지 않습니다. 삭제된 열 데이터를 제거하려면 다음을 수행합니다.
- 파일을 다시 작성하는 데 사용합니다 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(...)
)
스키마 진화 활성화
WITH SCHEMA EVOLUTION을 사용하거나 mergeSchema을 true(으)로 설정하여 기존 테이블에 INSERT하거나 MERGE하려는 데이터의 스키마를 기반으로 스키마를 변경할 수 있습니다.
다음 방법 중 하나를 사용하여 스키마 진화를 사용하도록 설정합니다.
-
INSERT문에는INSERT WITH SCHEMA EVOLUTION구문을 사용합니다. - with
MERGE문에는MERGE WITH SCHEMA EVOLUTION구문을 사용합니다.WITH SCHEMA EVOLUTIONSQL 구문 또는.withSchemaEvolution()Azure Databricks API에서 사용합니다. - 일괄 처리 쓰기 또는 스트리밍 쓰기에 대한 옵션을 설정합니다.
mergeSchema.option("mergeSchema", "true")을 개별 쓰기 작업에 설정합니다. -
Spark 구성(레거시)을 설정합니다.
spark.databricks.delta.schema.autoMerge.enabled전체 SparkSession에 대해 설정합니다true.
Databricks는 Spark 구성을 설정하는 대신 WITH SCHEMA EVOLUTION 구문이나 mergeSchema 옵션을 사용하여 각 쓰기 작업에 대해 스키마 변화를 사용하도록 설정하는 것이 좋습니다.
옵션 또는 구문을 사용하여 쓰기 작업에서 스키마 진화를 사용하도록 설정하는 경우 Spark 구성보다 우선합니다.
추가적인 열을 포함하기 위해 쓰기에 대한 스키마 진화를 활성화합니다.
스키마 진화를 사용하도록 설정하면 원본 쿼리에 있지만 대상 테이블에서 누락된 열이 쓰기 트랜잭션의 일부로 자동으로 추가됩니다. 스키마 진화 활성화를 참조하세요.
다음을 고려하십시오.
- 새 열을 추가할 때 대소문자가 유지됩니다.
- 테이블 스키마의 끝에 새 열이 추가됩니다.
- 추가 열이 구조체에 있는 경우 대상 테이블의 구조체 끝에 추가됩니다.
INSERT SQL을 사용하여 스키마 진화
INSERT 문에서 WITH SCHEMA EVOLUTION 절을 사용하여 스키마 진화가 가능하도록 합니다.
INSERT WITH SCHEMA EVOLUTION INTO target_table
SELECT * FROM source_table
쿼리 source_table 가 대상 테이블에 없는 열을 반환하는 경우 해당 열이 스키마에 target_table 자동으로 추가됩니다. 기존 행은 새 열의 값으로 채 NULL 워집니다.
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대상 테이블과 원본 테이블 간의 스키마 불일치를 해결할 수 있습니다. 다음 두 가지 경우를 처리합니다.
열은 원본 테이블에 있지만 대상 테이블에는 없으며 삽입 또는 업데이트 작업의 할당에서 이름으로 지정됩니다. 또는
UPDATE SET *또는INSERT *동작이 있습니다.해당 열이 대상 스키마에 추가되고 해당 값은 원본의 해당 열에서 채워집니다.
이는 병합 원본의 열 이름과 구조가 대상 할당과 정확히 일치하는 경우에만 적용됩니다.
새 열은 원본 스키마에 있어야 합니다. 작업 절에 새 열을 할당해도 해당 열은 정의되지 않습니다.
다음 예제는 스키마 진화를 허용합니다.
-- 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>>)를 이용한 스키마 진화를 사용할 수 있습니다.
MERGESQL, 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 * |
테이블 스키마가 변경되지 않은 상태로 유지되고, key 및 value 열만 업데이트/삽입됩니다. |
테이블 스키마가 (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 동작이 오류를 throw합니다. |
테이블 스키마가 (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 열이 존재하지 않아 오류를 throw합니다. |
테이블 스키마가 (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 열이 존재하지 않아 오류를 throw합니다. |
테이블 스키마가 (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 이하 오류입니다.
병합이 있는 열 제외
Databricks Runtime 12.2 LTS 이상에서는 병합 조건에서 절을 사용하여 EXCEPT 열을 명시적으로 제외할 수 있습니다. 키워드의 EXCEPT 동작은 스키마 진화를 사용하는지 여부에 따라 달라집니다.
스키마 진화가 비활성화되면 EXCEPT 키워드는 대상 테이블의 열 목록에 적용되며, UPDATE 또는 INSERT 작업에서 열을 제외할 수 있습니다. 제외된 열은 null로 설정됩니다.
스키마 진화를 EXCEPT 사용하도록 설정하면 키워드가 원본 테이블의 열 목록에 적용되고 스키마 진화에서 열을 제외할 수 있습니다. 원본에 있는 새 열이 대상 테이블에 없더라도 EXCEPT 절에 나열되어 있으면 대상 스키마에 추가되지 않습니다. 대상에 이미 있는 제외된 열은 null로 설정됩니다.
MERGE이 포함된 EXCLUDE의 예
다음 예제에서는 이 구문을 보여 줍니다.
| 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 열이 존재하지 않아 오류를 throw합니다. |
일치하는 행은 필드를 현재 날짜로 설정 last_updated 하여 업데이트됩니다. 필드 review 가 대상 테이블에 추가되지만 internal_count 필드는 무시됩니다. 새로운 행에는 last_updated가 null로 설정됩니다. |
Spark 구성을 사용하여 스키마 진화 사용(레거시)
현재 SparkSession의 모든 쓰기 작업에 대해 스키마 진화를 사용하도록 Spark 구성 spark.databricks.delta.schema.autoMerge.enabledtrue 을 설정할 수 있습니다.
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 구성보다 우선합니다.
테이블 스키마 바꾸기
기본적으로 테이블의 데이터를 덮어쓰는 것은 스키마를 덮어쓰지 않습니다.
replaceWhere 없이 mode("overwrite")를 사용해 테이블을 덮어쓸 때에도, 기록되는 데이터의 스키마를 덮어쓰고 싶을 수 있습니다.
테이블의 스키마 및 분할을 바꾸려면 옵션을 다음으로 overwriteSchema설정합니다true.
df.write.option("overwriteSchema", "true")
Note
동적 파티션 덮어쓰기를 사용할 때 overwriteSchema를 true로 지정할 수 없습니다.
(레거시)를 사용한 partitionOverwriteMode 동적 파티션 덮어쓰기를 참조하세요.