다이나믹 테이블 수정하기

다이나믹 테이블 수정하기

요구사항이 바뀌면 다이나믹 테이블 파이프라인도 수정해야 합니다. 정의(definition)를 갱신하거나, 컬럼을 추가하거나, 리프레시 모드를 바꾸는 작업이 여기에 해당해요. 이 페이지에서는 이런 변경을 안전하게 하는 방법, 재초기화(reinitialization)가 일어나는 조건, 그리고 변경이 파이프라인을 따라 아래로 어떻게 전파되는지 설명합니다.

출처: Snowflake 문서

본문

팁: 변경을 실행하기 전에 해당 변경이 테이블의 다음 리프레시에 어떤 영향을 줄지 미리 볼 수 있어요. Predict refresh behavior를 참고하세요.

참고: Frozen 영역은 이전에 immutability constraint라고 불렸고, FROZEN WHERE 절은 이전에 IMMUTABLE WHERE 문법을 사용했어요. 이전 IMMUTABLE WHERE 문법은 계속 지원되며, SHOW DYNAMIC TABLES도 여전히 immutable_where 컬럼을 사용합니다.

전체 문법 레퍼런스는 ALTER DYNAMIC TABLE과 CREATE DYNAMIC TABLE을 참고하세요.

가장 흔한 수정, 즉 정의 변경은 CREATE OR ALTER DYNAMIC TABLE로 할 수 있어요.

CREATE OR ALTER는 선언적이고 멱등(idempotent)한 명령입니다. 다이나믹 테이블의 최종 상태 전체를 기술하면, Snowflake가 필요한 변경을 계산해서 적용해요. 다이나믹 테이블이 없으면 새로 만들고, 있으면 정의와 일치하도록 변경하며, 이미 일치하면 그대로 둡니다. CREATE DYNAMIC TABLE을 참고하세요.

-- create.mdx의 dt_orders 정의와 동일하되, 새 컬럼이 하나 추가된 예시
CREATE OR ALTER DYNAMIC TABLE dt_orders
  TARGET_LAG = '10 minutes'
  WAREHOUSE = transform_wh
  REFRESH_MODE = INCREMENTAL
AS
  SELECT
    order_id,
    customer_id,
    order_date,
    ...
    order_date::DATE AS order_day  -- 새 컬럼
  FROM raw_orders
  WHERE order_status != 'returned';

CREATE OR ALTER는 기존 다이나믹 테이블의 정의를 갱신합니다. 정의 변경 후의 다음 리프레시는 전체 재초기화가 됩니다. 이 과정에서도 사용자는 기존 다이나믹 테이블을 계속 읽을 수 있어요.

CREATE OR REPLACE DYNAMIC TABLE

CREATE OR REPLACE DYNAMIC TABLE은 DROP 의미론을 포함해 정의를 수정할 수도 있어요. CREATE OR REPLACE는 원자적(atomic)입니다. 사용자(하위 다이나믹 테이블과 동시 읽기)는 이전 정의나 새 정의 중 하나만 볼 수 있고, 중간 상태는 절대 볼 수 없어요. 문장이 실패하면 원래의 다이나믹 테이블은 그대로 유지됩니다. CREATE OR REPLACE는 새 정의가 이전 정의와 동일해도 항상 재초기화를 유발해요. INITIALIZE = ON_SCHEDULE를 쓰면 초기 리프레시가 끝날 때까지 테이블이 비어 있고, 기본값인 INITIALIZE = ON_CREATE에서는 초기 리프레시가 끝날 때까지 CREATE 문이 블록됩니다.

ALTER DYNAMIC TABLE vs. CREATE OR REPLACE vs. CREATE OR ALTER

일부 속성은 ALTER DYNAMIC TABLE로 변경할 수 있어요. 다른 속성은 CREATE OR ALTER나 CREATE OR REPLACE가 필요합니다. CREATE OR REPLACE는 항상 다이나믹 테이블을 재초기화하고, CREATE OR ALTER는 일부 변경에서만 재초기화해요. 아래 표를 참고해 어떤 방식을 써야 하는지 판단하세요.

변경 ALTER CREATE OR ALTER CREATE OR REPLACE 재초기화?
TARGET_LAG ✓ ✓ — 아니요
WAREHOUSE ✓ ✓ — 아니요
REFRESH_MODE — ✓ ✓ 전환 방식에 따라 다름 (Refresh mode transitions 참고)
스케줄링 상태 (suspend/resume) ✓ — — 아니요
SCHEDULER (ENABLE/DISABLE) ✓ ✓ — 아니요
클러스터링 키 ✓ ✓ — 아니요 (reclustering 유발, 컴퓨팅 비용 발생)
정의 — ✓ ✓ 예
컬럼 추가/제거 — ✓ ✓ 예
데이터 타입 변경 — — ✓ 예
이름 변경 ✓ — — 아니요
Swap ✓ — — 아니요
Frozen 영역 (추가/확장) ✓ ✓ — 아니요
Frozen 영역 (축소) ✓ ✓ — 예 (활성 영역 재초기화)
Frozen 영역 (제거) ✓ ✓ — 예 (전체 재초기화)
스토리지 라이프사이클 정책 (단축) — — — 아니요 (CREATE OR REPLACE STORAGE LIFECYCLE POLICY 사용)
스토리지 라이프사이클 정책 (연장/추가) ✓ — — 예 (이전에 만료된 행을 다시 처리해야 함)
스토리지 라이프사이클 정책 (제거/수정) ✓ — — 예 (전체 재초기화)

이 표는 가장 흔히 바꾸는 속성들만 보여줍니다. ALTER로 변경 가능한 전체 목록은 ALTER DYNAMIC TABLE을 참고하세요.

재초기화가 필요 없는 속성 변경에는 ALTER DYNAMIC TABLE을 사용합니다:

ALTER DYNAMIC TABLE dt_orders SET
  TARGET_LAG = '5 minutes'
  WAREHOUSE = transform_wh_xl;

정의 수정하기

다이나믹 테이블의 정의를 바꾸려면 CREATE OR ALTER DYNAMIC TABLE이나 CREATE OR REPLACE DYNAMIC TABLE을 사용하세요. 정의 수정을 위한 ALTER 문법은 없어요.

다음 예시는 CREATE OR ALTER를 이용해 discount_pct 컬럼을 추가하고 필터 로직을 바꿉니다:

CREATE OR ALTER DYNAMIC TABLE dt_orders
  TARGET_LAG = '10 minutes'
  WAREHOUSE = transform_wh
  REFRESH_MODE = INCREMENTAL
AS
  SELECT
    order_id,
    customer_id,
    order_date,
    ...
    COALESCE(discount_pct, 0) AS discount_pct,  -- 새 컬럼
    order_status
  FROM raw_orders
  WHERE order_status NOT IN ('returned', 'cancelled');  -- 수정된 필터

전체 dt_orders 컬럼 목록은 Create a dynamic table을 참고하세요.

재초기화를 유발하는 변경

재초기화는 모든 데이터를 처음부터 다시 처리하는 강제 전체 리프레시(full refresh)입니다. 다음 변경들이 재초기화를 유발해요:

트리거 참고
다이나믹 테이블 자체에 대한 CREATE OR REPLACE 정의 변경, 컬럼 추가
상위 base table, 다이나믹 테이블, 뷰, UDF에 대한 CREATE OR REPLACE dbt 실행에서 base table 재생성
base table의 참조 컬럼을 drop 후 재추가 이름과 타입이 같아도 해당
base table에 행 액세스/마스킹 정책 추가 또는 제거 증가분(incremental) 다이나믹 테이블만 해당
구조적 경계를 넘는 REFRESH_MODE 변경 (예: FULL에서 INCREMENTAL로) 아래 Refresh mode transitions 참고
복제된 증가분 다이나믹 테이블의 장애 조치(failover) 내부 변경 추적 상태가 failover 중에 이전되지 않아 전체 리프레시 필요
다이나믹 테이블의 frozen 영역 제거 또는 수정 이전에 얼린 영역을 다시 처리해야 함. Frozen regions and backfill 참고
다이나믹 테이블의 스토리지 라이프사이클 정책 제거 또는 수정 이전에 만료된 행을 다시 처리해야 함. refresh-history의 REINIT_REASON 컬럼이 변경 사유를 알려줌. Use storage lifecycle policies with dynamic tables 참고

중요: 다이나믹 테이블이 MAX_DATA_EXTENSION_TIME_IN_DAYS를 넘어 stale해지면 자동으로 리프레시할 수 없어요. 위 트리거들과 달리 이 경우는 자동 재초기화가 아니라 수동 개입이 필요합니다. CREATE OR REPLACE DYNAMIC TABLE로 테이블을 다시 만들어 복구하세요.

참고: 커스텀 증가분 다이나믹 테이블은 상위 스키마 변경 시 재초기화하지 않아요. 대신 다음 리프레시가 컴파일 에러로 실패합니다. 복구하려면 갱신된 REFRESH USING 정의와 함께 CREATE OR ALTER를 사용하세요 (CREATE OR ALTER 이후의 다음 리프레시는 증가분이 될 수 있어요). BACKFILL FROM이나 START AT를 바꿔야 한다면 CREATE OR REPLACE를 대신 사용하세요. 자세한 내용은 Custom incrementalization을 참고하세요.

Refresh mode transitions

CREATE OR ALTER DYNAMIC TABLE로 REFRESH_MODE를 바꿀 수 있어요. 효과는 전환 방식에 따라 다릅니다:

전환 효과
INCREMENTAL ↔ ADAPTIVE 리프레시 모드만 갱신. 재초기화 없음. 두 모드는 동일한 구조적 요건(행 ID 컬럼과 변경 추적)을 공유
FULL ↔ INCREMENTAL 또는 FULL ↔ ADAPTIVE 리프레시 모드 갱신 + 재초기화 유발. 두 모드는 구조적 요건이 다름
구체적 모드 (INCREMENTAL, FULL, ADAPTIVE) → AUTO 또는 AUTO → 동일한 구체적 모드 메타데이터만 변경. 다이나믹 테이블은 현재 리프레시 모드를 계속 사용

AUTO는 생성 시점에 INCREMENTAL 또는 FULL로 결정됩니다 (ADAPTIVE는 절대 되지 않아요). 결정된 후에는 AUTO와 결정된 구체적 모드 사이의 전환이 재초기화나 동작 변화를 유발하지 않습니다.

프로덕션 모드 확정하기

다이나믹 테이블 정의를 버전 관리로 관리하면서 프로토타이핑 중에 AUTO를 썼다면, 프로덕션 승격 전에 정의를 명시적 모드(예: INCREMENTAL)로 바꾸는 게 좋아요. 이전 정의에서 배포된 기존 다이나믹 테이블은 현재 리프레시 모드를 유지하고, 갱신된 정의에서 생성된 새 테이블은 명시적 모드를 사용하므로 비용이 예측 가능해집니다. 자세한 안내는 Choose a refresh mode를 참고하세요.

재초기화를 유발하지 않는 변경

다음 변경들은 재초기화를 유발하지 않습니다:

변경 안전한 이유
ALTER … SET TARGET_LAG 또는 CREATE OR ALTER 스케줄링 동작만 변경, 데이터에는 영향 없음
ALTER … SET WAREHOUSE 또는 CREATE OR ALTER 리프레시를 실행할 웨어하우스만 변경, 데이터에는 영향 없음
ALTER … SUSPEND / RESUME 스케줄링 상태만 변경, 데이터에는 영향 없음
ALTER … CLUSTER BY 또는 CREATE OR ALTER reclustering 추가, 데이터 재처리는 없음
ALTER … SET EXECUTE AS USER 또는 CREATE OR ALTER 리프레시 실행 컨텍스트만 변경, 데이터에는 영향 없음
base table에 미참조 컬럼 추가/삭제 다이나믹 테이블의 컬럼 lineage에 영향 없음

참고: base table에서 컬럼을 drop하고 같은 이름과 데이터 타입으로 다시 추가하면, Snowflake는 이를 새 컬럼으로 취급해 재초기화를 유발합니다.

변경이 하위로 전파되는 방식

상위 다이나믹 테이블을 수정하면 그 효과는 변경 유형에 따라 달라집니다.

속성 변경 (ALTER): 상위 다이나믹 테이블에서 TARGET_LAG나 WAREHOUSE를 변경해도 하위 다이나믹 테이블의 재초기화는 유발되지 않아요. 하위 테이블은 snapshot-isolated 리프레시를 통해 상위 테이블의 출력을 계속 읽습니다. 다만 TARGET_LAG 변경은 상위 데이터가 리프레시되는 빈도를 바꿔, 파이프라인 전체의 신선도에 영향을 줄 수 있어요.

정의/모드 변경 (CREATE OR ALTER): CREATE OR ALTER로 상위 다이나믹 테이블의 정의, 리프레시 모드, 또는 frozen 영역을 변경하면 그 상위 테이블의 재초기화가 유발될 수 있어요. 하지만 하위 다이나믹 테이블의 재초기화는 유발하지 않습니다. 하위 테이블은 snapshot-isolated 리프레시를 통해 상위 출력을 계속 읽어요.

재생성 (CREATE OR REPLACE): 상위 다이나믹 테이블을 재생성하면 그 테이블의 재초기화가 유발됩니다. 하위 증가분 다이나믹 테이블도 다음 리프레시에서 재초기화되는데, 상위 테이블이 새 객체로 취급되기 때문이에요. 하위 전체 리프레시 다이나믹 테이블은 매 리프레시마다 이미 모든 데이터를 다시 처리하므로 영향이 없습니다.

리프레시 모드 호환성: 증가분 하위 다이나믹 테이블은 전체 리프레시 상위 다이나믹 테이블에 의존할 수 없어요. 단, 상위 테이블이 시스템 파생 고유 키(system-derived unique key)나 frozen 영역을 통해 행 수준 변경 정보를 제공한다면 예외입니다 (Optimize input data for dynamic tables 참고).

상위 테이블의 리프레시 모드를 INCREMENTAL에서 FULL로 바꾸면 하위 테이블이 호환되는지 확인하세요:

SHOW DYNAMIC TABLES LIKE 'dt_orders%';
+-----------------+--------------+
| name            | refresh_mode |
+-----------------+--------------+
| DT_ORDERS_DAILY | INCREMENTAL  |
+-----------------+--------------+

하위 테이블이 INCREMENTAL인데 상위가 이제 FULL이면, 하위 테이블을 REFRESH_MODE = FULL로 재생성하거나, 상위 테이블이 시스템 파생 고유 키를 제공하거나 frozen 영역이 있는지 확인하세요.

SELECT * 로 자동 스키마 진화

SELECT *를 사용하는 다이나믹 테이블은 대부분의 base table 스키마 변경을 자동으로 수용합니다. Snowflake는 대부분의 스키마 변경을 재초기화 없이 증가분으로 처리해요.

자동 처리 (증가분 리프레시):

  • base table에 컬럼 추가
  • base table에서 컬럼 삭제
  • 타입 확장 (예: NUMBER(10) → NUMBER(38))
  • 타입 축소 (예: NUMBER(20) → NUMBER(10)). 다이나믹 테이블은 원래의 더 넓은 타입을 유지
  • 여러 번의 연속 스키마 변경
  • 변경이 전체 파이프라인 그래프에 전파됨. 각 하위 다이나믹 테이블은 다음 리프레시에서 변경을 증가분으로 반영: 종속적인 재초기화는 없음

새로 추가된 컬럼의 기존 행에는 NULL이 들어갑니다. backfill은 없어요.

자동 처리되지만 재초기화를 유발:

  • 같은 이름의 컬럼 drop 후 재추가 (타입과 무관)
  • DEFAULT 값이 있는 컬럼 추가
  • 다이나믹 테이블이 SELECT *, expr AS alias를 사용할 때 base table에 컬럼 추가 (새 컬럼이 순번 위치를 바꿈)
  • 다이나믹 테이블이 읽는 뷰 정의 변경 (CREATE OR ALTER VIEW)
  • CREATE OR REPLACE TABLE로 base table 교체 (새 테이블 객체 생성)

수동 개입 필요 (리프레시 실패):

  • 호환되지 않는 타입 변경 (예: TEXT를 제자리에서 INT로)
  • CREATE 문에 명시적 컬럼 스펙이 있는 다이나믹 테이블
  • CLUSTER BY가 참조하는 컬럼 삭제

SELECT * EXCLUDE (col1, col2)를 사용하면 특정 컬럼만 빼면서 나머지에 대한 자동 진화는 유지할 수 있어요:

CREATE OR REPLACE DYNAMIC TABLE dt_orders
  TARGET_LAG = DOWNSTREAM
  WAREHOUSE = transform_wh
  REFRESH_MODE = INCREMENTAL
AS
  SELECT * EXCLUDE (order_status, unit_price)
  FROM raw_orders
  WHERE order_status != 'cancelled';

raw_orders에 새 컬럼이 추가되면 dt_orders는 다음 리프레시에서 자동으로 반영합니다. 삭제된 컬럼은 CREATE OR REPLACE 없이 사라져요.

명시적 컬럼 목록을 쓰는 게 나은 경우:

  • 특정 컬럼을 변환/이름 변경/캐스트해야 할 때
  • 출력에서 컬럼 순서를 제어해야 할 때
  • base table에 전파하면 안 되는 민감 컬럼이 있을 때

스키마 진화가 예상대로 동작하지 않으면 Snowflake Support에 문의해 계정에서 스키마 진화 기능이 활성화되어 있는지 확인하세요.

안전한 스키마 진화 워크플로우

대부분의 스키마 변경에는 CREATE OR ALTER DYNAMIC TABLE이나 CREATE OR REPLACE DYNAMIC TABLE을 사용하세요.

CREATE OR ALTER는 스키마 갱신을 즉시 수행합니다. 추가된 컬럼은 바로 보이지만 다음 리프레시 전까지는 null 값을 포함하고, 이름 변경된 컬럼(마지막 컬럼만 해당)도 다음 리프레시 전까지는 null 값을 포함한 채 즉시 보여요.

CREATE OR REPLACE는 원자적입니다. Snowflake가 대체본을 숨은 테이블로 만들어 초기 리프레시를 실행한 뒤 원자적으로 교체합니다. 하위 테이블은 이전 버전이나 새 버전 중 하나만 보고, 중간 상태는 절대 보지 못해요. 하위 증가분 다이나믹 테이블은 다음 리프레시에서 자동으로 재초기화됩니다.

재초기화 없이 스키마를 진화시키려면 frozen 영역을 사용해 안정적인 데이터를 보호하면서 활성 영역을 수정하세요.

고급: 하위 테이블을 일시중지해야 하는 경우

대부분의 경우 하위 테이블을 일시중지할 필요는 없어요. 다음 경우에만 일시중지를 고려하세요:

  • dbt 동시 교체 경쟁: 관련 테이블에서 CREATE OR REPLACE를 실행하는 여러 dbt 스레드가 일시적 해석(resolution) 실패를 만들 수 있어요. 배치 전에 하위 테이블을 일시중지하고 후에 재개하세요.
  • 비용 제어: 하위 테이블 재초기화가 비싸다면(대용량 데이터), 일시중지해 비수기 시간대로 재초기화를 미루세요.
  • 하위 다이나믹 테이블의 스트림: 다이나믹 테이블의 스트림은 테이블이 재초기화될 때 offset을 잃어요. 하위 테이블을 일시중지하고 스트림을 먼저 소비한 뒤 재개하세요.

일시중지할 때는 모든 단계를 같은 세션에서 완료하세요. 일시중지된 테이블이 MAX_DATA_EXTENSION_TIME_IN_DAYS를 넘기면 완전히 재생성해야 합니다.

다이나믹 테이블 이름 변경

참조하던 기존 스크립트를 유지하면서 다이나믹 테이블을 교체하고 싶을 때 이름 변경이 유용해요. ALTER DYNAMIC TABLE ... RENAME TO로 이름을 변경합니다.

중요: 다이나믹 테이블을 이름 변경해도 하위 다이나믹 테이블 정의의 참조는 갱신되지 않아요. dt_orders_daily가 이름으로 dt_orders를 참조한다면, dt_orders를 이름 변경하면 다음 리프레시에서 dt_orders_daily가 깨집니다. 상위 의존성을 이름 변경한 뒤에는 하위 테이블을 재생성하세요.

다이나믹 테이블 Swap

Swap은 두 다이나믹 테이블의 이름과 내용을 원자적으로 교환합니다. 블루-그린 배포에 유용해요: 새 버전을 만들고 검증한 뒤 제자리에 swap하면 됩니다.

다이나믹 테이블은 다른 다이나믹 테이블과만 swap할 수 있어요. ALTER DYNAMIC TABLE <name> SWAP WITH <other_name>으로 실행합니다.

Swap 후에는 dt_orders가 dt_orders_v2가 갖고 있던 데이터와 정의를 갖게 되고, 그 반대도 마찬가지예요. 이름으로 dt_orders를 참조하던 하위 테이블은 이제 swap된 버전을 읽습니다.

클러스터링 키 추가

ALTER DYNAMIC TABLE로 기존 다이나믹 테이블의 클러스터링 키를 추가하거나 변경할 수 있고, 재초기화 없이 즉시 적용됩니다. 클러스터링 키는 증가분 또는 전체 리프레시 모드를 사용하는 다이나믹 테이블의 쿼리 성능과 리프레시 속도를 모두 향상시킬 수 있어요.

클러스터링 키 선택과 효과 측정에 대한 안내는 Optimize queries for incremental refresh와 Optimize input data for dynamic tables를 참고하세요.

더 알아보기 (Learn more)