CREATE TABLE
CREATE TABLE
새로운 테이블을 만들어요. 기본적으로 테이블은 현재 서버에만 만들어집니다.
분산 DDL 쿼리는 ON CLUSTER 절로 구현되며, 이는 별도로 설명됩니다.
출처: 문서
본문
Syntax forms
이 쿼리는 사용 사례에 따라 다양한 문법 형태를 가질 수 있어요.
Create a table with an explicit schema
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [NULL|NOT NULL] [DEFAULT|MATERIALIZED|EPHEMERAL|ALIAS expr1] [COMMENT 'comment for column'] [compression_codec] [TTL expr1],
name2 [type2] [NULL|NOT NULL] [DEFAULT|MATERIALIZED|EPHEMERAL|ALIAS expr2] [COMMENT 'comment for column'] [compression_codec] [TTL expr2],
...
) ENGINE = engine
[COMMENT 'comment for table']
db 데이터베이스(또는 db가 설정되지 않으면 현재 데이터베이스)에 table_name이라는 테이블을, 대괄호 안에 지정된 구조와 engine 엔진으로 만들어요.
테이블의 구조는 컬럼 설명, 보조 인덱스, 프로젝션, 제약 조건의 목록입니다. 엔진이 primary key를 지원하면 테이블 엔진의 파라미터로 표시됩니다.
컬럼 설명은 가장 단순한 경우 name type입니다. 예: RegionID UInt32.
타입 뒤에 오는 수정자 — COMMENT, compression_codec, STATISTICS, TTL, COLLATE, PRIMARY KEY 및 컬럼별 SETTINGS — 는 어떤 순서로든 쓸 수 있으며, 각각 최대 한 번입니다. 예를 들어 RegionID UInt32 CODEC(ZSTD) COMMENT 'comment for column'와 RegionID UInt32 COMMENT 'comment for column' CODEC(ZSTD)는 같습니다. SHOW CREATE TABLE은 컬럼 선언을 정규화한다는 점에 유의하세요. 남아 있는 수정자는 항상 표준 순서 COMMENT, CODEC, STATISTICS, TTL, COLLATE, SETTINGS로 출력되는 반면, 컬럼별 PRIMARY KEY는 컬럼 선언 밖의 테이블 수준 PRIMARY KEY 절로 이동합니다.
기본 값에 대한 표현식도 정의할 수 있습니다(아래 참조).
필요하면 하나 이상의 키 표현식으로 기본 키를 지정할 수 있습니다.
컬럼과 테이블에 주석을 추가할 수 있습니다.
Create a table with an existing tables schema
CREATE TABLE [IF NOT EXISTS] [db2.]table_clone AS [db.]table [ENGINE = engine]
ClickHouse는 기존 테이블의 스키마와 데이터를 복사하는 기능을 지원합니다.
기존 테이블의 스키마를 복제하기 위해:
이것은 다른 테이블과 같은 구조의 테이블을 만듭니다.
Create a table with an existing tables schema and data
기존 테이블의 스키마와 데이터를 복제하기 위해:
CREATE TABLE [IF NOT EXISTS] [db2.]table_clone CLONE AS [db.]table [ENGINE = engine]
이것은 기존 테이블과 같은 스키마와 데이터를 가진 테이블을 만들어요. 새 테이블이 생성된 후 db.table의 모든 파티션이 그것에 연결(attach)됩니다. 즉, db.table의 데이터가 생성 시점에 db2.table_clone으로 복제됩니다.
CLONE AS는 대상 데이터베이스가 Replicated 데이터베이스 엔진을 사용할 때 지원되지 않습니다.
대신 다음을 사용하세요:
CREATE TABLE [IF NOT EXISTS] [db2.]table_clone AS [db.]table [ENGINE = engine];
ALTER TABLE [db2.]table_clone ATTACH PARTITION ALL FROM [db.]table;
두 기능 모두 테이블에 다른 엔진을 지정할 수 있습니다.
엔진이 지정되지 않으면 원래 테이블(db.table)과 같은 엔진이 사용됩니다.
Create a table with a table function
CREATE TABLE [IF NOT EXISTS] [db.]table_name AS table_function()
지정된 table function의 결과와 같은 테이블을 만들어요. 만들어진 테이블은 지정된 해당 테이블 함수와 같은 방식으로도 동작합니다.
Create a table with a SELECT query
CREATE TABLE [IF NOT EXISTS] [db.]table_name[(name1 [type1], name2 [type2], ...)] ENGINE = engine AS SELECT ...
SELECT 쿼리의 결과 같은 구조로 engine 엔진의 테이블을 만들고 SELECT의 데이터로 채워요. 컬럼 설명을 명시적으로 지정할 수도 있습니다.
테이블이 이미 존재하고 IF NOT EXISTS가 지정되면 쿼리는 아무것도 하지 않습니다.
쿼리의 ENGINE 절 뒤에 다른 절이 올 수 있습니다. table engines의 설명에서 테이블을 만드는 방법에 대한 자세한 문서를 참조하세요.
Example
CREATE TABLE t1 (x String) ENGINE = Memory AS SELECT 1;
SELECT x, toTypeName(x) FROM t1;
┌─x─┬─toTypeName(x)─┐
│ 1 │ String │
└───┴───────────────┘
Specify column default values
컬럼 설명은 DEFAULT expr, MATERIALIZED expr, 또는 ALIAS expr 형태의 기본 값 표현식을 지정할 수 있어요. 예: URLDomain String DEFAULT domain(URL).
표현식 expr은 선택 사항이에요. 생략하면 컬럼 타입을 명시적으로 지정해야 하며, 기본 값은 숫자 컬럼의 경우 0, 문자열 컬럼의 경우 ''(빈 문자열), 배열 컬럼의 경우 [](빈 배열), 날짜 컬럼의 경우 1970-01-01, nullable 컬럼의 경우 NULL이 됩니다.
기본 값 컬럼의 컬럼 타입은 생략할 수 있으며, 그 경우 expr의 타입에서 추론됩니다. 예를 들어 EventDate DEFAULT toDate(EventTime) 컬럼의 타입은 date가 됩니다.
데이터 타입과 기본 값 표현식이 모두 지정되면 표현식을 지정된 타입으로 변환하는 암시적 타입 캐스팅 함수가 삽입됩니다. 예: Hits UInt32 DEFAULT 0은 내부적으로 Hits UInt32 DEFAULT toUInt32(0)로 표현됩니다.
기본 값 표현식 expr은 임의의 테이블 컬럼과 상수를 참조할 수 있어요. ClickHouse는 테이블 구조의 변경이 표현식 계산에 루프를 도입하지 않는지 확인합니다. INSERT의 경우 표현식이 해석 가능한지 — 계산할 수 있는 모든 컬럼이 전달되었는지 — 확인합니다.
DEFAULT
DEFAULT expr
일반 기본 값이에요. INSERT 쿼리에서 그러한 컬럼의 값이 지정되지 않으면 expr로부터 계산됩니다.
Example:
CREATE OR REPLACE TABLE test
(
id UInt64,
updated_at DateTime DEFAULT now(),
updated_at_date Date DEFAULT toDate(updated_at)
)
ENGINE = MergeTree
ORDER BY id;
INSERT INTO test (id) VALUES (1);
SELECT * FROM test;
┌─id─┬──────────updated_at─┬─updated_at_date─┐
│ 1 │ 2023-02-24 17:06:46 │ 2023-02-24 │
└────┴─────────────────────┴─────────────────┘
MATERIALIZED
MATERIALIZED expr
머티리얼라이즈드 표현식이에요. 그러한 컬럼의 값은 행이 삽입될 때 지정된 머티리얼라이즈드 표현식에 따라 자동으로 계산됩니다. INSERT 중에 값을 명시적으로 지정할 수 없습니다.
또한 이 타입의 기본 값 컬럼은 SELECT *의 결과에 포함되지 않습니다. 이는 SELECT *의 결과가 항상 INSERT를 사용해 테이블에 다시 삽입될 수 있다는 불변식을 보존하기 위해서입니다. 이 동작은 asterisk_include_materialized_columns 설정으로 비활성화할 수 있습니다.
Example:
CREATE OR REPLACE TABLE test
(
id UInt64,
updated_at DateTime MATERIALIZED now(),
updated_at_date Date MATERIALIZED toDate(updated_at)
)
ENGINE = MergeTree
ORDER BY id;
INSERT INTO test VALUES (1);
SELECT * FROM test;
┌─id─┐
│ 1 │
└────┘
SELECT id, updated_at, updated_at_date FROM test;
┌─id─┬──────────updated_at─┬─updated_at_date─┐
│ 1 │ 2023-02-24 17:08:08 │ 2023-02-24 │
└────┴─────────────────────┴─────────────────┘
SELECT * FROM test SETTINGS asterisk_include_materialized_columns=1;
┌─id─┬──────────updated_at─┬─updated_at_date─┐
│ 1 │ 2023-02-24 17:08:08 │ 2023-02-24 │
└────┴─────────────────────┴─────────────────┘
EPHEMERAL
EPHEMERAL [expr]
에페메럴 컬럼이에요. 이 타입의 컬럼은 테이블에 저장되지 않으며 SELECT할 수 없습니다. 에페메럴 컬럼의 유일한 목적은 다른 컬럼의 기본 값 표현식을 만드는 것입니다.
명시적으로 지정된 컬럼이 없는 insert는 이 타입의 컬럼을 건너뜁니다. 이는 SELECT *의 결과가 항상 INSERT를 사용해 테이블에 다시 삽입될 수 있다는 불변식을 보존하기 위해서입니다.
Example:
CREATE OR REPLACE TABLE test
(
id UInt64,
unhexed String EPHEMERAL,
hexed FixedString(4) DEFAULT unhex(unhexed)
)
ENGINE = MergeTree
ORDER BY id;
INSERT INTO test (id, unhexed) VALUES (1, '5a90b714');
SELECT
id,
hexed,
hex(hexed)
FROM test
FORMAT Vertical;
Row 1:
──────
id: 1
hexed: Z��
hex(hexed): 5A90B714
ALIAS
ALIAS expr
계산된 컬럼(동의어)이에요. 이 타입의 컬럼은 테이블에 저장되지 않으며 INSERT 값을 넣을 수 없습니다.
SELECT 쿼리가 이 타입의 컬럼을 명시적으로 참조하면 쿼리 시점에 expr로부터 값이 계산됩니다. 기본적으로 SELECT *는 ALIAS 컬럼을 제외합니다. 이 동작은 asterisk_include_alias_columns 설정으로 비활성화할 수 있습니다.
ALTER 쿼리로 새 컬럼을 추가할 때, 이 컬럼의 기존 데이터는 쓰이지 않습니다. 대신, 새 컬럼에 대한 값이 없는 기존 데이터를 읽을 때 기본적으로 표현식이 즉시 계산됩니다. 그러나 표현식을 실행하는 데 쿼리에 표시되지 않은 다른 컬럼이 필요하면 그 컬럼들이 추가로 읽히는데, 필요로 하는 데이터 블록에 대해서만 읽힙니다.
테이블에 새 컬럼을 추가했지만 나중에 기본 표현식을 변경하면, 기존 데이터에 사용되는 값이 바뀝니다(값이 디스크에 저장되지 않은 데이터에 대해). 백그라운드 병합을 실행할 때 병합되는 파트 중 하나에서 누락된 컬럼의 데이터가 병합된 파트에 쓰인다는 점에 유의하세요.
중첩 데이터 구조의 요소에 기본 값을 설정하는 것은 불가능합니다.
CREATE OR REPLACE TABLE test
(
id UInt64,
size_bytes Int64,
size String ALIAS formatReadableSize(size_bytes)
)
ENGINE = MergeTree
ORDER BY id;
INSERT INTO test VALUES (1, 4678899);
SELECT id, size_bytes, size FROM test;
┌─id─┬─size_bytes─┬─size─────┐
│ 1 │ 4678899 │ 4.46 MiB │
└────┴────────────┴──────────┘
SELECT * FROM test SETTINGS asterisk_include_alias_columns=1;
┌─id─┬─size_bytes─┬─size─────┐
│ 1 │ 4678899 │ 4.46 MiB │
└────┴────────────┴──────────┘
Use NULL or NOT NULL modifiers
컬럼 정의에서 데이터 타입 뒤의 NULL 및 NOT NULL 수정자는 컬럼이 Nullable이 되는 것을 허용하거나 허용하지 않아요.
타입이 Nullable이 아니고 NULL이 지정되면 Nullable로 취급됩니다. NOT NULL이 지정되면 그렇지 않습니다. 예를 들어 INT NULL은 Nullable(INT)와 같습니다. 타입이 Nullable이고 NULL 또는 NOT NULL 수정자가 지정되면 예외가 던져집니다.
data_type_default_nullable 설정도 참고하세요.
Primary key
테이블을 만들 때 기본 키를 정의할 수 있어요. 기본 키는 두 가지 방법으로 지정할 수 있습니다:
컬럼 목록 안
CREATE TABLE [db.]table_name
(
name1 type1, name2 type2, ...,
PRIMARY KEY(expr1[, expr2,...])
)
ENGINE = engine;
컬럼 목록 밖
CREATE TABLE [db.]table_name
(
name1 type1, name2 type2, ...
)
ENGINE = engine
PRIMARY KEY(expr1[, expr2,...]);
한 쿼리에서 두 방법을 결합할 수 없습니다.
Specify table constraints
컬럼 설명과 함께 제약 조건을 정의할 수 있습니다:
CONSTRAINT
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [compression_codec] [TTL expr1],
...
CONSTRAINT constraint_name_1 CHECK boolean_expr_1,
...
) ENGINE = engine
boolean_expr_1은 어떤 불리언 표현식이든 될 수 있어요. 테이블에 제약 조건이 정의되면 INSERT 쿼리의 모든 행에 대해 각각이 검사됩니다. 어떤 제약 조건이 충족되지 않으면 서버는 제약 이름과 검사 표현식과 함께 예외를 발생시킵니다.
많은 양의 제약 조건을 추가하면 큰 INSERT 쿼리의 성능에 부정적인 영향을 줄 수 있어요.
모든 테이블의 기존 제약 조건은 system.constraints 테이블로 검사할 수 있습니다.
ASSUME
ASSUME 절은 참으로 가정되는 CONSTRAINT를 테이블에 정의하는 데 사용돼요. 이 제약 조건은 옵티마이저가 SQL 쿼리의 성능을 향상시키는 데 사용할 수 있습니다.
users_a 테이블을 만들 때 ASSUME CONSTRAINT가 사용된 이 예시를 보세요:
CREATE TABLE users_a (
uid Int16,
name String,
age Int16,
name_len UInt8 MATERIALIZED length(name),
CONSTRAINT c1 ASSUME length(name) = name_len
)
ENGINE=MergeTree
ORDER BY (name_len, name);
여기서 ASSUME CONSTRAINT는 length(name) 함수가 항상 name_len 컬럼의 값과 같다고 단언하는 데 사용됩니다. 이는 쿼리에서 length(name)이 호출될 때마다 ClickHouse가 그것을 name_len으로 교체할 수 있다는 뜻이며, length() 함수 호출을 피하므로 더 빠를 수 있어요.
그런 다음 SELECT name FROM users_a WHERE length(name) < 5; 쿼리를 실행할 때 ClickHouse는 ASSUME CONSTRAINT 덕분에 그것을 SELECT name FROM users_a WHERE name_len < 5로 최적화할 수 있습니다. 이로 각 행의 name 길이 계산을 피하므로 쿼리가 더 빨라질 수 있어요.
ASSUME CONSTRAINT는 제약 조건을 강제하지 않으며, 단지 옵티마이저에게 제약 조건이 성립함을 알릴 뿐입니다. 제약 조건이 실제로 성립하지 않으면 쿼리 결과가 올바르지 않을 수 있습니다. 따라서 ASSUME CONSTRAINT는 제약 조건이 참이라고 확신할 때만 사용해야 합니다.
Define storage time with TTL
값의 저장 시간을 정의해요. MergeTree 계열 테이블에서만 지정할 수 있습니다. 자세한 설명은 TTL for columns and tables를 참조하세요.
Select column compression codecs
기본적으로 ClickHouse는 자체 관리 버전에서 lz4 압축을, ClickHouse Cloud에서 zstd를 적용합니다. CREATE TABLE 쿼리에서 각 개별 컬럼의 압축 방법을 정의할 수도 있습니다:
CREATE TABLE codec_example
(
dt Date CODEC(ZSTD),
ts DateTime CODEC(LZ4HC),
float_value Float32 CODEC(NONE),
double_value Float64 CODEC(LZ4HC(9)),
value Float32 CODEC(Delta, ZSTD)
)
ENGINE = <Engine>
...
사용 가능한 일반 목적, 특수 및 암호화 코덱은 Column compression codecs를 참고하세요.
Create temporary tables
ClickHouse는 세션이 끝나면 사라지는 임시 테이블을 지원합니다. 자세한 내용은 CREATE TEMPORARY TABLE을 참고하세요.
Update a table atomically with REPLACE TABLE
REPLACE 문은 테이블을 원자적으로 업데이트할 수 있게 해줘요. 자세한 내용은 REPLACE TABLE을 참고하세요.
Add a table comment
테이블을 만들 때 주석을 추가할 수 있어요.
Syntax
CREATE TABLE [db.]table_name
(
name1 type1, name2 type2, ...
)
ENGINE = engine
COMMENT 'Comment'
COMMENT 절은 PARTITION BY, ORDER BY, 저장소 특정 SETTINGS 같은 저장소 관련 절 뒤에 지정되어야 합니다.
COMMENT 절 뒤에는 max_threads 같은 쿼리 특정 SETTINGS만 파싱되고, 저장소 관련 설정은 파싱되지 않습니다.
즉 올바른 절 순서는 이렇습니다:
ENGINE- 저장소 절
COMMENT- 쿼리 설정(있는 경우)
Example
CREATE TABLE t1 (x String) ENGINE = Memory COMMENT 'The temporary table';
SELECT name, comment FROM system.tables WHERE name = 't1';
┌─name─┬─comment─────────────┐
│ t1 │ The temporary table │
└──────┴─────────────────────┘