Delta Lake 테이블 스키마 업데이트
Delta Lake를 사용하면 테이블의 스키마를 업데이트할 수 있습니다. 지원되는 변경 유형은 다음과 같습니다.
- 새 열 추가(임의의 위치에서)
- 기존 열 순서 다시 지정
- 기존 열 이름 바꾸기
이러한 변경은 DDL을 사용하여 명시적으로 수행하거나 DML을 사용하여 암시적으로 수행할 수 있습니다.
Important
델타 테이블 스키마에 대한 업데이트는 모든 동시 델타 쓰기 작업과 충돌하는 작업입니다.
델타 테이블 스키마를 업데이트하면 해당 테이블에서 읽는 스트림이 종료됩니다. 스트림을 계속하려면 다시 시작해야 합니다. 권장 방법은 구조적 스트리밍에 대한 프로덕션 고려 사항을 참조하세요.
스키마를 명시적으로 업데이트하여 열 추가
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 열 매핑을 사용하여 열 이름 바꾸기 및 삭제를 참조하세요.
Important
메타데이터에서 열을 삭제해도 파일의 열에 대한 기본 데이터는 삭제되지 않습니다. 삭제된 열 데이터를 제거하려면 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
. 병합에 대한 스키마 진화 구문을 참조하세요. - 현재 SparkSession에
true
대해 Spark confspark.databricks.delta.schema.autoMerge.enabled
를 설정합니다.
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 병합을 위한 자동 스키마 진화
스키마 진화를 통해 사용자는 병합에서 대상 테이블과 원본 테이블 간의 스키마 불일치를 해결할 수 있습니다. 다음 두 가지 경우를 처리합니다.
- 원본 테이블의 열이 대상 테이블에 없습니다. 새 열이 대상 스키마에 추가되고 해당 값은 원본 값을 사용하여 삽입되거나 업데이트됩니다.
- 대상 테이블의 열이 원본 테이블에 없습니다. 대상 스키마는 변경되지 않은 상태로 유지됩니다. 추가 대상 열의 값은 변경되지 않은 상태로 유지되거나(for
UPDATE
)로NULL
INSERT
설정됩니다.
자동 스키마 진화를 수동으로 사용하도록 설정해야 합니다. 스키마 진화 사용을 참조하세요.
참고 항목
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 또는 델타 테이블 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 * |
대상 열 old_value 가 원본에 없기 때문에 UPDATE 및 INSERT 동작이 오류를 throw합니다. |
테이블 스키마가 (key, old_value, new_value) 로 변경됩니다. 일치 항목이 있는 기존 레코드는 원본에서 new_value 변경되지 않은 상태로 업데이트 old_value 됩니다. 새 레코드는 지정된 레코드 및 에 대해 삽입됩니다 key NULL new_value .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 |
new_value 열이 대상 테이블에 없기 때문에 UPDATE 가 오류를 throw합니다. |
테이블 스키마가 (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) |
new_value 열이 대상 테이블에 없기 때문에 INSERT 가 오류를 throw합니다. |
테이블 스키마가 (key, old_value, new_value) 로 변경됩니다. 새 레코드는 지정된 레코드 및 에 대해 삽입됩니다 key NULL new_value .old_value 기존 레코드는 NULL 변경되지 않은 상태로 두 old_value 기 위해 new_value 입력되었습니다. 참고 (1)를 참조하세요. |
(1) 이 동작은 Databricks Runtime 12.2 LTS 이상에서 사용할 수 있습니다. 이 조건의 Databricks Runtime 11.3 LTS 이하 오류입니다.
Delta Lake 병합을 사용하여 열 제외
Databricks Runtime 12.2 LTS 이상에서는 병합 조건에서 절을 사용하여 EXCEPT
열을 명시적으로 제외할 수 있습니다. 키워드의 EXCEPT
동작은 스키마 진화를 사용하는지 여부에 따라 달라집니다.
스키마 진화를 EXCEPT
사용하지 않도록 설정하면 키워드가 대상 테이블의 열 목록에 적용되며 열 또는 INSERT
작업에서 UPDATE
열을 제외할 수 있습니다. 제외된 열은 .로 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 하여 업데이트됩니다. 새 행은 에 대한 id 값을 사용하여 삽입됩니다 title . 제외된 필드는 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) |
internal_count 열이 대상 테이블에 없기 때문에 INSERT 가 오류를 throw합니다. |
일치하는 행은 필드를 현재 날짜로 설정 last_updated 하여 업데이트됩니다. 필드 review 가 대상 테이블에 추가되지만 internal_count 필드는 무시됩니다. 삽입된 새 행이 .로 null 설정되었습니다last_updated . |
스키마 업데이트에서 NullType
열 처리
Parquet는 NullType
을 지원하지 않으므로 델타 테이블에 쓸 때 NullType
열이 DataFrame에서 삭제되지만 스키마에는 계속 저장됩니다. 해당 열에 대해 다른 데이터 형식을 받으면 Delta Lake에서 스키마를 새 데이터 형식에 병합합니다. Delta Lake에서 기존 열에 대해 NullType
을 받으면 이전 스키마가 유지되고 쓰기 중에 새 열이 삭제됩니다.
스트리밍의 NullType
은 지원되지 않습니다. 스트리밍을 사용할 때 스키마를 설정해야 하므로 이는 매우 드문 경우입니다. NullType
은 ArrayType
및 MapType
과 같은 복합 형식에도 허용되지 않습니다.
테이블 스키마 바꾸기
기본적으로 테이블의 데이터를 덮어쓰더라도 스키마는 덮어쓰지 않습니다. replaceWhere
없이 mode("overwrite")
를 사용하여 테이블을 덮어쓰는 경우 여전히 쓰고 있는 데이터의 스키마를 덮어쓰려고 할 수 있습니다. overwriteSchema
옵션을 true
로 설정하여 테이블의 스키마 및 분할을 바꿉니다.
df.write.option("overwriteSchema", "true")
Important
동적 파티션 덮어쓰기를 사용할 때는 true
지정할 overwriteSchema
수 없습니다.