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-byteswrite.parquet.row-group-size-bytes 테이블 속성은 파티셔닝된 테이블에서 존중되지 않고 오류를 내요. 대신 ignore_target_file_size_for_partitioned_tables 또는 ignore_row_group_size_for_partitioned_tablestrue로 설정해 무시할 수 있어요.

테이블 속성 (Table Properties)

Iceberg 테이블 속성은 생성 시 WITH 절로 설정할 수 있어요. format-versionlocation 키는 특별히 인식되고, 다른 키-값 쌍은 테이블 속성으로 저장돼요:

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';

UPDATEDELETE는 파티셔닝된 테이블과 파티셔닝되지 않은 테이블 모두에서 지원돼요. 이들은 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)

  • UPDATEDELETEpositional deletes만 써요; copy-on-write는 지원되지 않아요.
  • UPDATEDELETEmerge-on-read 시맨틱만 지원해요. 테이블이 write.update.modewrite.delete.modemerge-on-read가 아닌 다른 값으로 설정하면 연산이 실패해요.
  • write.target-file-size-byteswrite.parquet.row-group-size-bytes 테이블 속성은 파티셔닝된 테이블에서 존중되지 않아요(Partitioning 참조).

더 알아보기 (Learn more)