ALTER COLUMN

ALTER COLUMN

이 페이지에서는 ALTER ... COLUMN 문으로 테이블 구조를 변경하는 방법을 다뤄요. 컬럼 추가, 삭제, 이름 변경, 값 초기화, 주석, 타입 수정, Enum 값 추가, 구체화(materialize) 등을 설명해요.

출처: 문서

본문

테이블 구조를 변경할 수 있는 쿼리 집합이에요. 문법:

ALTER [TEMPORARY] TABLE [db].name [ON CLUSTER cluster] ADD|DROP|RENAME|CLEAR|COMMENT|{MODIFY|ALTER}|MATERIALIZE COLUMN ...

쿼리에서 쉼표로 구분된 하나 이상의 동작 목록을 지정해요. 각 동작은 컬럼에 대한 연산이에요. 지원되는 동작은 다음과 같아요:

이 동작들은 아래에서 자세히 설명돼요.

ADD COLUMN

ADD COLUMN [IF NOT EXISTS] name [type] [default_expr] [COMMENT 'comment for column'] [codec] [STATISTICS] [TTL] [settings] [AFTER name_after | FIRST]

지정된 이름, 타입, codec, default_expr로 테이블에 새 컬럼을 추가해요(Default expressions 섹션 참고). 타입 뒤에 오는 수정자는 어떤 순서로든 쓸 수 있고 각각 최대 한 번만 쓸 수 있어요 — 컬럼 설명 참고. IF NOT EXISTS 절이 포함되면 컬럼이 이미 있어도 쿼리가 오류를 반환하지 않아요. AFTER name_after(다른 컬럼의 이름)를 지정하면 그 컬럼 뒤에 새 컬럼이 추가돼요. 테이블의 시작 부분에 컬럼을 추가하려면 FIRST 절을 사용해요. 그렇지 않으면 컬럼이 테이블 끝에 추가돼요. 동작 체인의 경우 name_after는 이전 동작 중 하나에서 추가된 컬럼의 이름일 수 있어요. 컬럼 추가는 데이터에 대한 어떤 작업도 하지 않고 테이블 구조만 변경해요. ALTER 후에 데이터가 디스크에 나타나지 않아요. 테이블에서 읽을 때 컬럼의 데이터가 없으면 기본값으로 채워져요(기본 표현식이 있으면 수행하고, 그렇지 않으면 0 또는 빈 문자열 사용). 컬럼은 데이터 파츠가 병합된 후 디스크에 나타나요(참고: MergeTree). 이 접근 방식을 통해 기존 데이터의 양을 늘리지 않고 ALTER 쿼리를 즉시 완료할 수 있어요. 예:

ALTER TABLE alter_test ADD COLUMN Added1 UInt32 FIRST;
ALTER TABLE alter_test ADD COLUMN Added2 UInt32 AFTER NestedColumn;
ALTER TABLE alter_test ADD COLUMN Added3 UInt32 AFTER ToDrop;
DESC alter_test FORMAT TSV;
Added1  UInt32
CounterID       UInt32
StartDate       Date
UserID  UInt32
VisitID UInt32
NestedColumn.A  Array(UInt8)
NestedColumn.S  Array(String)
Added2  UInt32
ToDrop  UInt32
Added3  UInt32

DROP COLUMN

DROP COLUMN [IF EXISTS] name

이름이 name인 컬럼을 삭제해요. IF EXISTS 절이 지정되면 컬럼이 없어도 쿼리가 오류를 반환하지 않아요. 파일시스템에서 데이터를 삭제해요. 전체 파일을 삭제하므로 쿼리는 거의 즉시 완료돼요. 컬럼이 materialized view에서 참조되면 삭제할 수 없어요. 그렇지 않으면 오류를 반환해요. 예:

ALTER TABLE visits DROP COLUMN browser

RENAME COLUMN

RENAME COLUMN [IF EXISTS] name to new_name

컬럼 name의 이름을 new_name으로 바꿔요. IF EXISTS 절이 지정되면 컬럼이 없어도 쿼리가 오류를 반환하지 않아요. 이름 변경은 밑에 있는 데이터를 다루지 않으므로 쿼리는 거의 즉시 완료돼요. 참고: 테이블의 키 표현식(ORDER BY 또는 PRIMARY KEY)에 지정된 컬럼은 이름을 바꿀 수 없어요. 그런 컬럼을 변경하려 하면 SQL Error [524]가 발생해요. 예:

ALTER TABLE visits RENAME COLUMN webBrowser TO browser

CLEAR COLUMN

CLEAR COLUMN [IF EXISTS] name IN PARTITION partition_name

지정된 파티션에 대한 컬럼의 모든 데이터를 재설정해요. 파티션 이름 설정에 대한 자세한 내용은 How to set the partition expression 섹션을 참고하세요. IF EXISTS 절이 지정되면 컬럼이 없어도 쿼리가 오류를 반환하지 않아요. 예:

ALTER TABLE visits CLEAR COLUMN browser IN PARTITION tuple()

COMMENT COLUMN

COMMENT COLUMN [IF EXISTS] name 'Text comment'

컬럼에 주석을 추가해요. IF EXISTS 절이 지정되면 컬럼이 없어도 쿼리가 오류를 반환하지 않아요. 각 컬럼은 주석을 하나 가질 수 있어요. 컬럼에 주석이 이미 있으면 새 주석이 이전 주석을 덮어써요. 주석은 DESCRIBE TABLE 쿼리가 반환하는 comment_expression 컬럼에 저장돼요. 예:

ALTER TABLE visits COMMENT COLUMN browser 'This column shows the browser used for accessing the site.'

MODIFY COLUMN

MODIFY COLUMN [IF EXISTS] name
    [type] [default_expr] [COMMENT 'comment for column'] [codec] [STATISTICS] [TTL] [settings] [AFTER name_after | FIRST]
    | ADD ENUM VALUES ( 'name' [= number] [, ...] )
ALTER COLUMN [IF EXISTS] name
    TYPE [type] [default_expr] [COMMENT 'comment for column'] [codec] [STATISTICS] [TTL] [settings] [AFTER name_after | FIRST]
    | ADD ENUM VALUES ( 'name' [= number] [, ...] )

타입 뒤에 오는 수정자는 어떤 순서로든 쓸 수 있고 각각 최대 한 번만 쓸 수 있어요 — 컬럼 설명 참고. 이 쿼리는 name 컬럼의 속성을 변경해요:

  • 타입

  • 기본 표현식

  • 압축 코덱

  • TTL

  • 컬럼 수준 설정

  • Enum/Enum8/Enum16 타입의 Enum 값

컬럼 압축 CODEC 수정 예시는 Column Compression Codecs에서 확인할 수 있어요. 컬럼 TTL 수정 예시는 Column TTL에서 확인할 수 있어요. 컬럼 수준 설정 수정 예시는 Column-level Settings에서 확인할 수 있어요. IF EXISTS 절이 지정되면 컬럼이 없어도 쿼리가 오류를 반환하지 않아요. 타입을 변경할 때 값은 toType 함수가 적용된 것처럼 변환돼요. 기본 표현식만 변경하면 쿼리는 복잡한 작업을 하지 않고 거의 즉시 완료돼요. 예:

ALTER TABLE visits MODIFY COLUMN browser Array(String)

컬럼 타입 변경은 유일하게 복잡한 동작이에요 — 데이터가 있는 파일의 내용을 변경해요. 큰 테이블의 경우 오랜 시간이 걸릴 수 있어요. 쿼리는 FIRST | AFTER 절로 컬럼 순서도 변경할 수 있어요. ADD COLUMN 설명 참고. 이 경우 컬럼 타입은 필수예요. 예:

CREATE TABLE users (
    c1 Int16,
    c2 String
) ENGINE = MergeTree
ORDER BY c1;

DESCRIBE users;
┌─name─┬─type───┬
│ c1   │ Int16  │
│ c2   │ String │
└──────┴────────┴

ALTER TABLE users MODIFY COLUMN c2 String FIRST;

DESCRIBE users;
┌─name─┬─type───┬
│ c2   │ String │
│ c1   │ Int16  │
└──────┴────────┴

ALTER TABLE users ALTER COLUMN c2 TYPE String AFTER c1;

DESCRIBE users;
┌─name─┬─type───┬
│ c1   │ Int16  │
│ c2   │ String │
└──────┴────────┴

ALTER 쿼리는 원자적이에요. MergeTree 테이블에서는 잠금이 없어요. 컬럼 변경을 위한 ALTER 쿼리는 복제돼요. 지침은 ZooKeeper에 저장된 다음 각 레플리카가 적용해요. 모든 ALTER 쿼리는 같은 순서로 실행돼요. 쿼리는 다른 레플리카에서 적절한 작업이 완료되기를 기다려요. 그러나 복제 테이블에서 컬럼을 변경하는 쿼리는 중단될 수 있으며, 모든 동작이 비동기로 수행돼요. Nullable 컬럼을 비-Nullable로 변경할 때 주의하세요. NULL 값이 없는지 확인해요. 그렇지 않으면 읽을 때 문제가 생길 수 있어요. 그런 경우 변경을 죽이고 컬럼을 다시 Nullable 타입으로 되돌리는 것이 해결 방법이에요.

MODIFY COLUMN REMOVE

컬럼 속성 중 하나를 제거해요: DEFAULT, ALIAS, MATERIALIZED, CODEC, COMMENT, TTL, SETTINGS. 문법:

ALTER TABLE table_name MODIFY COLUMN column_name REMOVE property;

예시 TTL 제거:

ALTER TABLE table_with_ttl MODIFY COLUMN column_ttl REMOVE TTL;

함께 보기

MODIFY COLUMN MODIFY SETTING

컬럼 설정을 수정해요. 문법:

ALTER TABLE table_name MODIFY COLUMN column_name MODIFY SETTING name=value,...;

예시 컬럼의 max_compress_block_size를 1MB로 수정:

ALTER TABLE table_name MODIFY COLUMN column_name MODIFY SETTING max_compress_block_size = 1048576;

MODIFY COLUMN RESET SETTING

컬럼 설정을 재설정하고, 테이블 CREATE 쿼리의 컬럼 표현식에서 설정 선언도 제거해요. 문법:

ALTER TABLE table_name MODIFY COLUMN column_name RESET SETTING name,...;

예시 컬럼 설정 max_compress_block_size를 기본값으로 재설정:

ALTER TABLE table_name MODIFY COLUMN column_name RESET SETTING max_compress_block_size;

MODIFY COLUMN ADD ENUM VALUES

Enum, Enum8, Enum16, Nullable(Enum), Nullable(Enum8), Nullable(Enum16) 타입의 컬럼에 새 값을 추가해요. 문법:

ALTER TABLE table_name MODIFY COLUMN enum_column_name ADD ENUM VALUES ('EnumName' [= number], ...);

예시 컬럼 enum_column_name에 두 값을 추가:

ALTER TABLE table_name MODIFY COLUMN enum_column_name ADD ENUM VALUES ('Hundred' = 100, 'HundredOne');

MATERIALIZE COLUMN

DEFAULT 또는 MATERIALIZED 값 표현식이 있는 컬럼을 구체화해요. ALTER TABLE table_name ADD COLUMN column_name MATERIALIZED로 구체화 컬럼을 추가하면, 구체화 값이 없는 기존 행은 자동으로 채워지지 않아요. MATERIALIZE COLUMN 문은 DEFAULT 또는 MATERIALIZED 표현식이 추가되거나 업데이트된 후(메타데이터만 업데이트하고 기존 데이터는 변경하지 않음) 기존 컬럼 데이터를 다시 쓰는 데 사용할 수 있어요. 정렬 키의 컬럼을 구체화하는 것은 정렬 순서를 깨뜨릴 수 있으므로 유효하지 않은 작업이에요. 변경(mutation)으로 구현돼요. 새롭거나 업데이트된 MATERIALIZED 값 표현식이 있는 컬럼의 경우 모든 기존 행이 다시 작성돼요. 새롭거나 업데이트된 DEFAULT 값 표현식이 있는 컬럼의 경우 동작은 ClickHouse 버전에 따라 달라져요:

  • ClickHouse < v24.2에서는 모든 기존 행이 다시 작성돼요.

  • ClickHouse >= v24.2에서는 DEFAULT 값 표현식이 있는 컬럼의 행 값이 삽입 시 명시적으로 지정되었는지, 아니면 그렇지 않은지(즉 DEFAULT 값 표현식에서 계산됨)를 구분해요. 값이 명시적으로 지정되었으면 ClickHouse는 그대로 유지해요. 값이 계산된 것이면 ClickHouse는 새롭거나 업데이트된 MATERIALIZED 값 표현식으로 변경해요.

문법:

ALTER TABLE [db.]table [ON CLUSTER cluster] MATERIALIZE COLUMN col [IN PARTITION partition | IN PARTITION ID 'partition_id'];
  • PARTITION을 지정하면 컬럼이 지정된 파티션에서만 구체화돼요.

예시

DROP TABLE IF EXISTS tmp;
SET mutations_sync = 2;
CREATE TABLE tmp (x Int64) ENGINE = MergeTree() ORDER BY tuple() PARTITION BY tuple();
INSERT INTO tmp SELECT * FROM system.numbers LIMIT 5;
ALTER TABLE tmp ADD COLUMN s String MATERIALIZED toString(x);

ALTER TABLE tmp MATERIALIZE COLUMN s;

SELECT groupArray(x), groupArray(s) FROM (select x,s from tmp order by x);

┌─groupArray(x)─┬─groupArray(s)─────────┐
│ [0,1,2,3,4]   │ ['0','1','2','3','4'] │
└───────────────┴───────────────────────┘

ALTER TABLE tmp MODIFY COLUMN s String MATERIALIZED toString(round(100/x));

INSERT INTO tmp SELECT * FROM system.numbers LIMIT 5,5;

SELECT groupArray(x), groupArray(s) FROM tmp;

┌─groupArray(x)─────────┬─groupArray(s)──────────────────────────────────┐
│ [0,1,2,3,4,5,6,7,8,9] │ ['0','1','2','3','4','20','17','14','12','11'] │
└───────────────────────┴────────────────────────────────────────────────┘

ALTER TABLE tmp MATERIALIZE COLUMN s;

SELECT groupArray(x), groupArray(s) FROM tmp;

┌─groupArray(x)─────────┬─groupArray(s)─────────────────────────────────────────┐
│ [0,1,2,3,4,5,6,7,8,9] │ ['inf','100','50','33','25','20','17','14','12','11'] │
└───────────────────────┴───────────────────────────────────────────────────────┘

MATERIALIZE COLUMN은 항상 컬럼 값을 다시 작성해요. 데이터 스킵 인덱스(텍스트 인덱스 포함)도 재구축되는지는 파트 레이아웃과 인덱스 저장 방식에 따라 달라져요:

  • 동시에 와이드 + 전체 저장인 파트에서는 컬럼만 다시 작성될 때 독립 스킵 인덱스 파일(및 텍스트 인덱스)이 자동으로 재구축되지 않아요. 인덱스가 의존하는 밑에 있는 컬럼 데이터를 변경한 후 인덱스 파일을 새로 고쳐야 한다면 ALTER TABLE ... MATERIALIZE INDEX를 실행해요.

  • 예외: skp_idx.packed에 포장된 일반 스킵 인덱스(기본 packed_skip_index_max_bytes 아래의 작은 스킵 인덱스 서브스트림; 전체 텍스트 인덱스는 이렇게 포장되지 않음)는 와이드 + 전체 저장 파트에서도 강제로 다시 계산돼요.

  • 와이드 + 전체 저장이 아닌 파트(컴팩트 + 전체, 컴팩트 + 포장, 와이드 + 포장 포함)에서는 변경이 전체 파트 다시 쓰기 경로를 취하고 해당 파트의 기존 보조 인덱스를 강제로 다시 계산해요. 작은 파츠는 기본적으로 전체 파트 저장을 사용하면서도 컴팩트인 경우가 흔하므로, 이 분기는 최근 삽입의 일반적인 경우예요.

별도로, ADD INDEX는 메타데이터만 업데이트해요: 기존(히스토리컬) 파츠에 새 인덱스(텍스트 인덱스 포함)를 즉시 구체화하지 않아요. 히스토리컬 파츠는 명시적 MATERIALIZE INDEX로, 또는 materialize_skip_indexes_on_merge가 활성화되고 인덱스가 exclude_materialize_skip_indexes_on_merge에 나열되지 않을 때 이후 병합으로 구체화돼요. 병합 설정을 기다리지 않고 인덱스를 구축해야 할 때 명시적 MATERIALIZE INDEX가 결정적 경로예요.

함께 보기

제한 사항 (Limitations)

ALTER 쿼리는 중첩 데이터 구조에서 개별 요소(컬럼)를 만들고 삭제할 수 있지만, 전체 중첩 데이터 구조는 만들 수 없어요. 중첩 데이터 구조를 추가하려면 name.nested_name 같은 이름과 Array(T) 타입의 컬럼을 추가할 수 있어요. 중첩 데이터 구조는 점 앞의 이름이 같은 여러 배열 컬럼과 동일해요. 이름에 점이 있는 컬럼의 이름 변경은 부분적으로 지원돼요. 점은 Nested 하위 컬럼 접근을 위해 예약되어 있으므로 접두사(부모 이름)는 그대로 유지해야 해요. 접미사(하위 컬럼 이름)만 변경할 수 있어요. 예를 들어 a.ba.c로 이름을 변경할 수 있지만, a.bb.d로 변경하면 Nested 부모 접두사가 바뀌므로 허용되지 않아요. 기본 키 또는 샘플링 키(ENGINE 표현식에 사용된 컬럼)의 컬럼 삭제는 지원되지 않아요. 기본 키에 포함된 컬럼의 타입 변경은 데이터가 수정되지 않는 경우에만 가능해요(예: Enum에 값을 추가하거나 DateTimeUInt32로 변경하는 것은 허용). ALTER 쿼리가 필요한 테이블 변경을 처리하기에 충분하지 않다면 새 테이블을 만들고 INSERT SELECT 쿼리로 데이터를 복사한 다음 RENAME 쿼리로 테이블을 전환하고 기존 테이블을 삭제할 수 있어요. ALTER 쿼리는 테이블의 모든 읽기와 쓰기를 차단해요. 즉, ALTER 쿼리 시점에 긴 SELECT가 실행 중이면 ALTER 쿼리는 그것이 완료되기를 기다려요. 동시에 같은 테이블에 대한 모든 새 쿼리는 이 ALTER가 실행되는 동안 기다려요. 데이터를 스스로 저장하지 않는 테이블(예: MergeDistributed)의 경우 ALTER는 테이블 구조만 변경하고 하위 테이블의 구조는 변경하지 않아요. 예를 들어 Distributed 테이블에 대해 ALTER를 실행하면 모든 원격 서버의 테이블에 대해서도 ALTER를 실행해야 해요.

더 알아보기 (Learn more)