테이블에 데이터를 삽입합니다.
구문
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] [SETTINGS ...] VALUES (v11, v12, v13), (v21, v22, v23), ...
삽입할 컬럼 목록은 (c1, c2, c3)를 사용하여 지정할 수 있습니다. 또한 *와 같은 컬럼 matcher 표현식이나 APPLY, EXCEPT, REPLACE와 같은 수정자도 사용할 수 있습니다.
예시로, 다음 테이블을 살펴보겠습니다:
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) ;
컬럼 목록에 기존 컬럼이 모두 포함되어 있지 않으면, 나머지 컬럼은 다음 값으로 채워집니다:
- 테이블 정의(table definition)에 지정된
DEFAULT 표현식으로 계산된 값
DEFAULT 표현식이 정의되어 있지 않으면 0과 빈 문자열
데이터는 ClickHouse가 지원하는 임의의 포맷으로 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는 데이터 앞의 모든 공백과 줄 바꿈 문자 하나(있는 경우)를 제거합니다. 쿼리를 작성할 때는 데이터가 공백으로 시작할 수 있으므로, 쿼리 연산자 뒤에서 줄을 바꿔 새 줄에 데이터를 넣는 것이 좋습니다.
예시:
INSERT INTO t FORMAT TabSeparated
11 Hello, world!
22 Qwerty
command-line client 또는 HTTP 인터페이스를 사용하면 쿼리와 별도로 데이터를 삽입할 수 있습니다.
INSERT 쿼리에 SETTINGS를 지정하려면 FORMAT 절 앞에 지정해야 합니다. FORMAT format_name 뒤의 모든 내용은 데이터로 처리되기 때문입니다. 예시는 다음과 같습니다.INSERT INTO table SETTINGS ... FORMAT format_name data_set
테이블에 제약 조건이 있으면, 삽입되는 데이터의 각 행에 대해 해당 표현식을 검사합니다. 이 제약 조건 중 하나라도 충족되지 않으면 서버에서 제약 조건 이름과 표현식이 포함된 예외를 발생시키고 쿼리를 중단합니다.
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;
-- 설정이 비활성화된 상태에서도 이 구문은 정상적으로 실행됩니다.
-- 테이블이 이미 존재하므로 삽입이 차단되지 않습니다.
INSERT INTO events VALUES (1, '14:30:25');
-- 단, Time 유형으로 새 테이블을 생성하면 실패합니다.
CREATE TABLE events_new
(
`id` UInt64,
`event_time` Time
)
ENGINE = MergeTree()
ORDER BY id; -- ERR: TYPE_TIME_TIME64_IS_NOT_ENABLED
따라서 최신 버전의 클라이언트(설정이 기본적으로 활성화된 상태)는 대상 테이블에 해당 컬럼 타입이 이미 있는 경우, 더 오래된 버전의 서버(설정이 비활성화된 상태)에 허용되지 않는 데이터 타입의 데이터를 삽입할 수 있습니다. 검증은 DML 수준이 아니라 DDL 수준에서 수행됩니다.
구문
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 절은 쿼리의 마지막에 지정해야 합니다.
널을 허용하지 않는 데이터 타입의 컬럼에 NULL 대신 기본값을 삽입하려면 insert_null_as_default 설정을 활성화하십시오.
INSERT는 CTE(common table expression)도 지원합니다. 예를 들어, 다음 두 SQL 문은 동일합니다:
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;
구문
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] FROM INFILE file_name [COMPRESSION type] [SETTINGS ...] [FORMAT format_name]
위 구문을 사용하면 클라이언트 측에 저장된 파일 하나 또는 여러 파일에서 데이터를 삽입할 수 있습니다. file_name과 type은 문자열 리터럴입니다. 입력 파일 포맷은 FORMAT 절에서 지정해야 합니다.
압축 파일도 지원됩니다. 압축 유형은 파일 이름의 확장자를 기준으로 감지됩니다. 또는 COMPRESSION 절에서 명시적으로 지정할 수도 있습니다. 지원되는 유형은 다음과 같습니다: 'none', 'gzip', 'deflate', 'br', 'xz', 'zstd', 'lz4', 'bz2'.
이 기능은 command-line client 및 clickhouse-local에서 사용할 수 있습니다.
예시
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 │
└────┴──────┘
글롭 패턴을 사용해 FROM INFILE로 여러 파일 처리하기
이 예시는 이전 예시와 매우 유사하지만, 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})와 기타 글롭 치환을 사용할 수 있습니다. 다음 세 가지 방법은 모두 위의 예시에 사용할 수 있습니다: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;
데이터는 테이블 함수로 참조되는 테이블에 삽입할 수 있습니다.
구문
INSERT INTO [TABLE] FUNCTION table_func ...
예시
다음 쿼리에서는 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() │
└─────┴───────────────────────┘
기본적으로 ClickHouse Cloud의 서비스는 고가용성을 위해 여러 레플리카를 제공합니다. 서비스에 연결하면 이러한 레플리카 중 하나에 연결됩니다.
INSERT가 성공하면 데이터가 기본 스토리지에 기록됩니다. 하지만 레플리카가 이러한 업데이트를 수신하는 데는 다소 시간이 걸릴 수 있습니다. 따라서 다른 연결을 사용해 다른 레플리카 중 하나에서 SELECT 쿼리를 실행하면 업데이트된 데이터가 아직 반영되지 않았을 수 있습니다.
select_sequential_consistency를 사용하면 레플리카가 최신 업데이트를 강제로 수신하도록 할 수 있습니다. 다음은 이 설정을 사용하는 SELECT 쿼리의 예시입니다.
SELECT .... SETTINGS select_sequential_consistency = 1;
select_sequential_consistency를 사용하면 ClickHouse Keeper(ClickHouse Cloud에서 내부적으로 사용됨)에 가해지는 부하가 증가하며, 서비스 부하에 따라 성능이 저하될 수 있습니다. 꼭 필요한 경우가 아니라면 이 설정은 활성화하지 않는 것이 좋습니다. 권장되는 방법은 동일한 session에서 읽기/쓰기를 수행하거나, 네이티브 프로토콜을 사용하는 클라이언트 드라이버(즉, 고정 연결을 지원하는 드라이버)를 사용하는 것입니다.
복제된 구성에서는 데이터가 복제된 뒤 다른 레플리카에 표시됩니다. 데이터 복제는 INSERT 직후 바로 시작되며(즉, 다른 레플리카에서 다운로드됨), 이 동작은 ClickHouse Cloud와 다릅니다. ClickHouse Cloud에서는 데이터가 즉시 공유 스토리지에 기록되고, 레플리카는 메타데이터 변경 사항을 구독합니다.
복제된 구성에서는 분산 합의를 위해 ClickHouse Keeper에 커밋해야 하므로 INSERTs에 상당한 시간(약 1초)이 걸릴 수 있다는 점에 유의하십시오. 또한 스토리지로 S3를 사용하면 지연 시간이 추가로 늘어납니다.
INSERT는 입력 데이터를 기본 키(primary key)로 정렬하고, 파티션 키(partition key)를 기준으로 파티션으로 나눕니다. 여러 파티션에 데이터를 한 번에 삽입하면 INSERT 쿼리 성능이 크게 저하될 수 있습니다. 이를 방지하려면 다음을 수행하세요:
- 한 번에 100,000행 정도의 비교적 큰 배치로 데이터를 추가합니다.
- ClickHouse에 업로드하기 전에 파티션 키를 기준으로 데이터를 그룹화합니다.
다음 경우에는 성능이 저하되지 않습니다:
- 데이터가 실시간으로 추가되는 경우
- 일반적으로 시간순으로 정렬된 데이터를 업로드하는 경우
작지만 빈번한 삽입 작업에서도 데이터를 비동기적으로 삽입할 수 있습니다. 이렇게 삽입된 데이터는 배치로 묶인 다음 안전하게 테이블에 삽입됩니다. 비동기 삽입을 사용하려면 async_insert 설정을 활성화하십시오.
async_insert 또는 Buffer 테이블 엔진을 사용하면 추가 버퍼링이 발생합니다.
대량의 데이터를 삽입할 때 ClickHouse는 “squashing”이라는 과정을 통해 쓰기 성능을 최적화합니다. 메모리에 있는 작은 데이터 블록은 디스크에 기록되기 전에 머지되어 더 큰 블록으로 합쳐집니다. squashing은 각 쓰기 작업에 수반되는 오버헤드를 줄여줍니다. 이 과정에서 삽입된 데이터는 ClickHouse가 각 max_insert_block_size개의 행을 기록한 후 쿼리할 수 있습니다.
관련 항목