Iceberg에 쓰기
Iceberg에 쓰기 (Writing to Iceberg)
iceberg 익스텐션은 Iceberg REST Catalog로 관리되는 Iceberg 테이블에 쓰기를 지원해요. 모든 쓰기 연산은 attach된 카탈로그를 거쳐 새 Iceberg 스냅샷으로 커밋돼요.
쓰기에는 attach된 카탈로그가 필요해요. 개요에 설명된 경로 기반
iceberg_scan인터페이스는 읽기 전용이에요. 쓰려면 먼저 Iceberg REST 카탈로그를 attach 해요.
아래 예시들은 카탈로그가 my_catalog로 attach되어 있다고 가정해요.
출처: 문서
본문
스키마와 테이블 생성 (Creating Schemas and Tables)
Iceberg 네임스페이스는 스키마로 노출돼요. 표준 SQL로 스키마와 테이블을 만들고 삭제할 수 있어요:
CREATE SCHEMA my_catalog.sales;
USE my_catalog.sales;
CREATE TABLE my_catalog.sales.events (
id INTEGER,
event_name VARCHAR,
event_time TIMESTAMP
);
-- 쿼리 결과로 테이블 만들기
CREATE TABLE my_catalog.sales.events_copy AS
FROM my_catalog.sales.events;
DROP TABLE my_catalog.sales.events_copy;
파티셔닝 (Partitioning)
테이블은 Iceberg 파티션 트랜스폼을 사용해 PARTITIONED BY 절로 파티셔닝할 수 있어요:
| Transform | 설명 |
|---|---|
⟨column⟩ |
Identity – 열 값으로 직접 파티셔닝. |
year(⟨column⟩), month(⟨column⟩), day(⟨column⟩), hour(⟨column⟩) |
날짜/타임스탬프 구성요소로 파티셔닝. |
bucket(⟨n⟩, ⟨column⟩) |
열을 n개 버킷으로 해시. |
truncate(⟨n⟩, ⟨column⟩) |
열 값을 폭 n으로 잘라냄. |
CREATE TABLE my_catalog.sales.events (
id INTEGER,
event_name VARCHAR,
event_time TIMESTAMP
)
PARTITIONED BY (day(event_time), bucket(16, id));
파티션 스펙은 ALTER TABLE ... SET PARTITIONED BY로 기존 테이블에서 변경할 수 있어요:
ALTER TABLE my_catalog.sales.events SET PARTITIONED BY (month(event_time));
write.target-file-size-bytes와write.parquet.row-group-size-bytes테이블 속성은 파티셔닝된 테이블에서 존중되지 않고 오류를 내요. 대신ignore_target_file_size_for_partitioned_tables또는ignore_row_group_size_for_partitioned_tables를true로 설정해 무시할 수 있어요.
테이블 속성 (Table Properties)
Iceberg 테이블 속성은 생성 시 WITH 절로 설정할 수 있어요. format-version과 location 키는 특별히 인식되고, 다른 키-값 쌍은 테이블 속성으로 저장돼요:
CREATE TABLE my_catalog.sales.events (a INTEGER)
WITH (
'format-version' = '2', -- Iceberg 포맷 버전 (2 또는 3)
'location' = 's3://my-bucket/events', -- 테이블 데이터의 기본 위치
'my.custom.property' = 'value'
);
기존 속성은 속성 함수로 조회·수정할 수 있어요:
-- 속성 보기
SELECT * FROM iceberg_table_properties(my_catalog.sales.events);
-- 속성 설정
CALL set_iceberg_table_properties(
my_catalog.sales.events,
MAP {'write.update.mode': 'merge-on-read', 'write.delete.mode': 'merge-on-read'}
);
-- 속성 제거
CALL remove_iceberg_table_properties(my_catalog.sales.events, ['my.custom.property']);
이에 상응하는 스키마(네임스페이스) 속성 함수는 Functions and Settings Reference를 참고해요.
데이터 삽입 (Inserting Data)
INSERT INTO my_catalog.sales.events
VALUES (1, 'click', TIMESTAMP '2026-06-01 10:00:00');
-- 쿼리 결과 삽입
INSERT INTO my_catalog.sales.events
SELECT * FROM source_table;
-- 위치가 아닌 이름으로 열 일치
INSERT INTO my_catalog.sales.events BY NAME
SELECT event_time, id, event_name FROM source_table;
업데이트와 삭제 (Updating and Deleting)
UPDATE my_catalog.sales.events SET event_name = 'view' WHERE id = 1;
DELETE FROM my_catalog.sales.events WHERE event_time < TIMESTAMP '2026-01-01';
UPDATE와 DELETE는 파티셔닝된 테이블과 파티셔닝되지 않은 테이블 모두에서 지원돼요. 이들은 merge-on-read 시맨틱을 사용하고 positional delete 파일을 써요; Limitations를 참고해요.
데이터 병합 (Merging Data)
MERGE INTO는 소스 관계에 대해 upsert를 수행해요. 조인 키는 두 번째 USING 절로 주어져요(MERGE INTO 문 참조):
MERGE INTO my_catalog.sales.events AS target
USING new_events AS source USING (id)
WHEN MATCHED THEN UPDATE SET event_name = source.event_name
WHEN NOT MATCHED THEN INSERT VALUES (source.id, source.event_name, source.event_time);
스키마 발전 (Evolving the Schema)
다음 ALTER TABLE 연산이 지원돼요:
ALTER TABLE my_catalog.sales.events ADD COLUMN source VARCHAR DEFAULT 'web';
ALTER TABLE my_catalog.sales.events DROP COLUMN source;
ALTER TABLE my_catalog.sales.events RENAME COLUMN id TO event_id;
ALTER TABLE my_catalog.sales.events ALTER COLUMN event_id TYPE BIGINT;
ALTER TABLE my_catalog.sales.events RENAME TO event_log;
ALTER TABLE my_catalog.sales.events ALTER COLUMN event_id SET DEFAULT 0;
ALTER TABLE my_catalog.sales.events ALTER COLUMN event_id DROP DEFAULT;
DuckDB와 Iceberg 사이 복사 (Copying Between DuckDB and Iceberg)
전체 DDL·DML 세트가 지원되므로, COPY FROM DATABASE가 Iceberg와 DuckDB 스토리지 사이에 어느 방향으로든 딥 카피를 수행할 수 있어요:
COPY FROM DATABASE duckdb_db TO my_catalog;
COPY FROM DATABASE my_catalog TO duckdb_db;
Iceberg 카탈로그를 DuckLake로 복사하려면 Interoperability with DuckLake를 참고해요.
제한 사항 (Limitations)
UPDATE와DELETE는 positional deletes만 써요; copy-on-write는 지원되지 않아요.UPDATE와DELETE는 merge-on-read 시맨틱만 지원해요. 테이블이write.update.mode나write.delete.mode를merge-on-read가 아닌 다른 값으로 설정하면 연산이 실패해요.write.target-file-size-bytes와write.parquet.row-group-size-bytes테이블 속성은 파티셔닝된 테이블에서 존중되지 않아요(Partitioning 참조).