INSERT INTO
INSERT INTO
테이블에 데이터를 삽입합니다.
Syntax
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] [SETTINGS ...] VALUES (v11, v12, v13), (v21, v22, v23), ...
(c1, c2, c3)로 삽입할 컬럼 목록을 지정할 수 있습니다. * 같은 컬럼 matcher와 APPLY, EXCEPT, REPLACE 같은 modifiers를 가진 표현식을 사용할 수도 있습니다.
예를 들어 다음 테이블을 고려해 보세요:
SHOW CREATE insert_select_testtable;
CREATE TABLE insert_select_testtable
(
`a` Int8,
`b` String,
`c` Int8
)
ENGINE = MergeTree()
ORDER BY a
INSERT INTO insert_select_testtable (*) VALUES (1, 'a', 1) ;
컬럼 b를 제외한 모든 컬럼에 데이터를 삽입하려면 EXCEPT 키워드를 사용할 수 있습니다. 위 문법과 관련해, 지정한 컬럼 수((c1, c3))만큼 많은 값을(VALUES (v11, v13)) 삽입해야 합니다:
INSERT INTO insert_select_testtable (* EXCEPT(b)) Values (2, 2);
SELECT * FROM insert_select_testtable;
┌─a─┬─b─┬─c─┐
│ 2 │ │ 2 │
└───┴───┴───┘
┌─a─┬─b─┬─c─┐
│ 1 │ a │ 1 │
└───┴───┴───┘
이 예시에서 두 번째 삽입된 행은 전달된 값으로 a와 c 컬럼이 채워지고, b는 기본값으로 채워진 것을 볼 수 있습니다. 기본 값을 삽입하려면 DEFAULT 키워드를 사용할 수도 있습니다:
INSERT INTO insert_select_testtable VALUES (1, DEFAULT, 1) ;
컬럼 목록이 모든 기존 컬럼을 포함하지 않으면 나머지 컬럼은 다음으로 채워집니다:
- 테이블 정의에 지정된
DEFAULT표현식에서 계산된 값. DEFAULT표현식이 정의되지 않으면 0과 빈 문자열.
데이터는 ClickHouse가 지원하는 어떤 format으로도 INSERT에 전달될 수 있습니다. 형식은 쿼리에서 명시적으로 지정해야 합니다:
INSERT INTO [db.]table [(c1, c2, c3)] FORMAT format_name data_set
예를 들어 다음 쿼리 형식은 기본 INSERT ... VALUES 버전과 동일합니다:
INSERT INTO [db.]table [(c1, c2, c3)] FORMAT Values (v11, v12, v13), (v21, v22, v23), ...
ClickHouse는 데이터 앞의 모든 공백과 줄 바꿈 하나(있으면)를 제거합니다. 쿼리를 만들 때 데이터를 쿼리 연산자 다음의 새 줄에 두는 것이 좋습니다. 데이터가 공백으로 시작하면 중요합니다.
Example:
INSERT INTO t FORMAT TabSeparated
11 Hello, world!
22 Qwerty
command-line client 또는 HTTP interface를 사용해 쿼리와 별도로 데이터를 삽입할 수 있습니다.
INSERT 쿼리에 SETTINGS를 지정하려면 FORMAT 절보다 앞에 해야 합니다. FORMAT format_name 뒤의 모든 것은 데이터로 처리되기 때문입니다. 예를 들어:
INSERT INTO table SETTINGS ... FORMAT format_name data_set
Constraints
테이블에 constraints가 있으면, 삽입된 각 데이터 행에 대해 그 표현식이 검사됩니다. 어떤 제약이 충족되지 않으면 — 서버가 제약 이름과 표현식을 포함한 예외를 발생시키고 쿼리가 중단됩니다.
Data Type Validation
ClickHouse는 허용된 데이터 타입(enable_time_time64_type, allow_suspicious_low_cardinality_types, allow_suspicious_fixed_string_types 등 설정으로 제어)을 테이블 생성(CREATE TABLE)과 스키마 수정(ALTER TABLE) 동안에만 검증하며, INSERT 동안에는 검증하지 않습니다.
즉 허용되지 않는 데이터 타입을 가진 테이블이 이미 존재하면, 서버에서 해당 설정이 비활성화되어 있어도 그 테이블에 데이터를 삽입할 수 있습니다. 이는 설계상 의도입니다. 테이블이 생성되면 타입 생성을 제어하는 설정으로 삽입을 막아서는 안 됩니다.
예를 들어:
SET enable_time_time64_type = 1;
CREATE TABLE events
(
`id` UInt64,
`event_time` Time
)
ENGINE = MergeTree()
ORDER BY id;
SET enable_time_time64_type = 0;
-- This works even though the setting is now disabled.
-- The table already exists, so inserts are not blocked.
INSERT INTO events VALUES (1, '14:30:25');
-- But creating a new table with the Time type will fail.
CREATE TABLE events_new
(
`id` UInt64,
`event_time` Time
)
ENGINE = MergeTree()
ORDER BY id; -- ERR: TYPE_TIME_TIME64_IS_NOT_ENABLED
결과적으로, 더 새로운 버전(설정이 기본적으로 활성화된)의 클라이언트는 대상 테이블이 이미 해당 컬럼 타입을 가지고 있다면, 더 오래된 버전(설정이 비활성화된)의 서버에 허용되지 않는 데이터 타입으로 데이터를 삽입할 수 있습니다. 검증은 DML 수준이 아니라 DDL 수준에서 시행됩니다.
Inserting the Results of SELECT
Syntax
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] SELECT ...
컬럼은 SELECT 절에서의 위치에 따라 매핑됩니다. 그러나 SELECT 표현식의 이름과 INSERT 대상 테이블의 이름은 다를 수 있습니다. 필요하면 타입 캐스팅이 수행됩니다.
Values 형식을 제외한 어떤 데이터 형식도 값에 now(), 1 + 2 같은 표현식을 설정하도록 허용하지 않습니다. Values 형식은 제한적인 표현식 사용을 허용하지만, 이 경우 비효율적인 코드로 실행되므로 권장되지 않습니다.
데이터 파트를 수정하는 다른 쿼리는 지원되지 않습니다: UPDATE, DELETE, REPLACE, MERGE, UPSERT, INSERT UPDATE.
하지만 ALTER TABLE ... DROP PARTITION을 사용해 이전 데이터를 삭제할 수 있습니다.
SELECT 절이 테이블 함수 input()을 포함하면 FORMAT 절을 쿼리 끝에 지정해야 합니다.
비nullable 데이터 타입의 컬럼에 NULL 대신 기본값을 삽입하려면 insert_null_as_default 설정을 활성화하세요.
INSERT는 CTE(공통 테이블 표현식)도 지원합니다. 예를 들어 다음 두 문은 동등합니다:
INSERT INTO x WITH y AS (SELECT * FROM numbers(10)) SELECT * FROM y;
WITH y AS (SELECT * FROM numbers(10)) INSERT INTO x SELECT * FROM y;
Inserting Data from a File
Syntax
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] FROM INFILE file_name [COMPRESSION type] [SETTINGS ...] [FORMAT format_name]
클라이언트 측에 저장된 파일(들)에서 데이터를 삽입하려면 위 문법을 사용하세요. file_name과 type은 문자열 리터럴입니다. 입력 파일 format은 FORMAT 절에 설정해야 합니다.
압축 파일이 지원됩니다. 압축 타입은 파일 이름의 확장자로 감지됩니다. 또는 COMPRESSION 절에서 명시적으로 지정할 수 있습니다. 지원되는 타입은: 'none', 'gzip', 'deflate', 'br', 'xz', 'zstd', 'lz4', 'bz2', 'snappy'입니다. snappy의 경우 와이어 형식은 snappy_mode 설정으로 선택됩니다(기본 basic).
이 기능은 command-line client와 clickhouse-local에서 사용할 수 있습니다.
Examples
Single file with FROM INFILE
command-line client를 사용해 다음 쿼리를 실행하세요:
echo 1,A > input.csv ; echo 2,B >> input.csv
clickhouse-client --query="CREATE TABLE table_from_file (id UInt32, text String) ENGINE=MergeTree() ORDER BY id;"
clickhouse-client --query="INSERT INTO table_from_file FROM INFILE 'input.csv' FORMAT CSV;"
clickhouse-client --query="SELECT * FROM table_from_file FORMAT PrettyCompact;"
┌─id─┬─text─┐
│ 1 │ A │
│ 2 │ B │
└────┴──────┘
Multiple files with FROM INFILE using globs
이 예시는 이전 예시와 매우 유사하지만 FROM INFILE 'input_*.csv를 사용해 여러 파일에서 삽입합니다.
echo 1,A > input_1.csv ; echo 2,B > input_2.csv
clickhouse-client --query="CREATE TABLE infile_globs (id UInt32, text String) ENGINE=MergeTree() ORDER BY id;"
clickhouse-client --query="INSERT INTO infile_globs FROM INFILE 'input_*.csv' FORMAT CSV;"
clickhouse-client --query="SELECT * FROM infile_globs FORMAT PrettyCompact;"
*로 여러 파일을 선택하는 것 외에도 범위({1,2} 또는 {1..9})와 기타 glob 치환을 사용할 수 있습니다. 다음 세 가지 모두 위 예시에서 동작합니다:
INSERT INTO infile_globs FROM INFILE 'input_*.csv' FORMAT CSV;
INSERT INTO infile_globs FROM INFILE 'input_{1,2}.csv' FORMAT CSV;
INSERT INTO infile_globs FROM INFILE 'input_?.csv' FORMAT CSV;
Inserting using a Table Function
table functions이 참조하는 테이블에 데이터를 삽입할 수 있습니다.
Syntax
INSERT INTO [TABLE] FUNCTION table_func ...
Example
다음 쿼리들에서 remote 테이블 함수가 사용됩니다:
CREATE TABLE simple_table (id UInt32, text String) ENGINE=MergeTree() ORDER BY id;
INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table)
VALUES (100, 'inserted via remote()');
SELECT * FROM simple_table;
┌──id─┬─text──────────────────┐
│ 100 │ inserted via remote() │
└─────┴───────────────────────┘
Inserting into ClickHouse Cloud
기본적으로 ClickHouse Cloud의 서비스는 고가용성을 위해 여러 복제본을 제공합니다. 서비스에 연결하면 이러한 복제본 중 하나와 연결이 설정됩니다.
INSERT가 성공한 후 데이터는 기본 저장소에 기록됩니다. 그러나 복제본이 이러한 갱신을 받는 데는 시간이 걸릴 수 있습니다. 따라서 다른 연결을 사용해 이 다른 복제본 중 하나에서 SELECT 쿼리를 실행하면, 갱신된 데이터가 아직 반영되지 않을 수 있습니다.
select_sequential_consistency를 사용해 복제본이 최신 갱신을 받도록 강제할 수 있습니다. 이 설정을 사용하는 SELECT 쿼리 예시입니다:
SELECT .... SETTINGS select_sequential_consistency = 1;
select_sequential_consistency를 사용하면 ClickHouse Keeper(ClickHouse Cloud가 내부적으로 사용)의 부하가 증가하고 서비스 부하에 따라 성능이 느려질 수 있습니다. 필요하지 않으면 이 설정을 활성화하지 않는 것이 좋습니다. 권장 방법은 같은 세션에서 읽기/쓰기를 실행하거나, 기본 프로토콜을 사용하는(따라서 스티키 연결을 지원하는) 클라이언트 드라이버를 사용하는 것입니다.
Inserting into a replicated setup
복제 설정에서 데이터는 복제된 후에 다른 복제본에서 볼 수 있습니다. 데이터는 INSERT 직후 복제되기(downloaded on other replicas) 시작됩니다. 이는 데이터가 공유 저장소에 즉시 기록되고 복제본이 메타데이터 변경을 구독하는 ClickHouse Cloud와 다릅니다.
복제 설정의 경우 INSERT는 분산 합의를 위해 ClickHouse Keeper에 커밋해야 하므로 상당한 시간(약 1초 단위)이 걸릴 수 있습니다. 저장소에 S3를 사용하는 것도 추가 지연을 더합니다.
Performance Considerations
INSERT는 입력 데이터를 기본 키로 정렬하고 파티션 키로 파티션으로 나눕니다. 한 번에 여러 파티션에 데이터를 삽입하면 INSERT 쿼리의 성능을 크게 떨어뜨릴 수 있습니다. 이를 피하려면:
- 한 번에 100,000행처럼 상당히 큰 배치로 데이터를 추가하세요.
- ClickHouse에 업로드하기 전에 파티션 키로 데이터를 그룹화하세요.
다음 경우에는 성능이 저하되지 않습니다:
- 데이터가 실시간으로 추가될 때.
- 보통 시간순으로 정렬된 데이터를 업로드할 때.
Asynchronous inserts
작고 빈번한 삽입으로 데이터를 비동기적으로 삽입하는 것이 가능합니다. 이러한 삽입의 데이터는 배치로 결합된 다음 안전하게 테이블에 삽입됩니다. 비동기 삽입을 사용하려면 async_insert 설정을 활성화하세요.
async_insert 또는 Buffer table engine을 사용하면 추가 버퍼링이 발생합니다.
Large or long-running inserts
많은 양의 데이터를 삽입할 때 ClickHouse는 "squashing"이라는 과정을 통해 쓰기 성능을 최적화합니다. 메모리의 작은 삽입 데이터 블록이 디스크에 쓰기 전에 더 큰 블록으로 병합되고 squashed됩니다. Squashing은 각 쓰기 연산과 관련된 오버헤드를 줄입니다. 이 과정에서 삽입된 데이터는 ClickHouse가 각 max_insert_block_size 행을 쓰기 완료한 후 쿼리할 수 있습니다.
See Also
- async_insert
- wait_for_async_insert
- wait_for_async_insert_timeout
- async_insert_max_data_size
- async_insert_busy_timeout_ms
async_insert_stale_timeout_ms