CSV 로딩
CSV 로딩
CSV 로딩, 즉 CSV 파일을 데이터베이스로 가져오는 작업은 아주 흔하면서도 의외로 까다로운 일이에요. CSV는 겉보기에 단순해 보이지만, 실제 파일들을 보면 일관되지 않은 부분이 많아서 로딩이 어려운 경우가 많죠. CSV 파일은 아주 다양한 형태로 존재하고, 종종 손상되어 있기도 하며, 스키마도 없습니다. CSV reader는 이런 모든 상황을 감당할 수 있어야 해요.
DuckDB의 CSV reader는 CSV sniffer를 이용해 CSV 파일을 분석한 뒤 어떤 설정 값(플래그)을 써야 할지 자동으로 추론합니다. 대부분의 상황에서 이 방식이 정확하게 동작하므로, 먼저 시도해 봐야 할 방법이에요. 드물게 CSV reader가 올바른 설정을 알아내지 못하는 상황에서는 설정을 직접 지정해서 CSV 파일을 제대로 파싱하도록 할 수 있습니다. 자세한 내용은 자동 감지 페이지를 참고하세요.
예시
다음 예시들은 flights.csv 파일을 사용합니다.
디스크에 있는 CSV 파일을 읽고, 옵션을 자동으로 추론할 때:
SELECT * FROM 'flights.csv';
read_csv 함수를 쓰고 옵션을 직접 지정할 때:
SELECT *
FROM read_csv('flights.csv',
delim = '|',
header = true,
columns = {
'FlightDate': 'DATE',
'UniqueCarrier': 'VARCHAR',
'OriginCityName': 'VARCHAR',
'DestCityName': 'VARCHAR'
});
표준 입력(stdin)에서 CSV를 읽고 옵션을 자동으로 추론할 때:
cat flights.csv | duckdb -c "SELECT * FROM read_csv('/dev/stdin')"
CSV 파일을 테이블로 읽어 들일 때:
CREATE TABLE ontime (
FlightDate DATE,
UniqueCarrier VARCHAR,
OriginCityName VARCHAR,
DestCityName VARCHAR
);
COPY ontime FROM 'flights.csv';
아니면 스키마를 직접 지정하지 않고 CREATE TABLE ... AS SELECT 문으로 테이블을 만들 수도 있어요:
CREATE TABLE ontime AS
SELECT * FROM 'flights.csv';
FROM-first 문법을 쓰면 SELECT *를 생략할 수 있어요.
CREATE TABLE ontime AS
FROM 'flights.csv';
파라미터
아래는 read_csv 함수에 넘길 수 있는 파라미터 목록입니다. 의미상 적용 가능한 곳에서는 이 파라미터들을 COPY 문에도 넘길 수 있어요.
| 이름 | 설명 | 타입 | 기본값 |
|---|---|---|---|
all_varchar |
타입 감지를 건너뛰고 모든 컬럼이 VARCHAR 타입이라고 가정합니다. 이 옵션은 read_csv 함수에서만 지원돼요. |
BOOL |
false |
allow_quoted_nulls |
따옴표로 감싼 값을 NULL 값으로 변환하는 것을 허용합니다. |
BOOL |
true |
auto_detect |
CSV 파라미터 자동 감지. | BOOL |
true |
auto_type_candidates |
스니퍼가 컬럼 타입을 감지할 때 고려하는 타입들입니다. VARCHAR 타입은 항상 폴백(fallback) 옵션으로 포함돼요. 예시를 참고하세요. |
TYPE[] |
기본 타입들 |
buffer_size |
파일을 읽을 때 사용하는 버퍼의 크기(바이트)입니다. 네 줄을 담을 수 있을 만큼은 커야 하며, 성능에 크게 영향을 줄 수 있어요. | BIGINT |
16 * max_line_size |
columns |
컬럼 이름과 타입을 구조체로 지정합니다 (예: {'col1': 'INTEGER', 'col2': 'VARCHAR'}). 이 옵션을 쓰면 스키마 자동 감지가 비활성화돼요. |
STRUCT |
(빈 값) |
comment |
주석을 시작하는 문자입니다. 주석 문자로 시작하는 줄(선택적으로 앞에 공백이 올 수 있음)은 완전히 무시되고, 주석 문자를 포함하는 다른 줄은 그 지점까지만 파싱돼요. | VARCHAR |
(빈 값) |
compression |
CSV 파일을 압축하는 데 쓰는 방법입니다. 기본적으로 파일 확장자에서 자동으로 감지됩니다 (예: t.csv.gz는 gzip, t.csv는 none). 옵션은 none, gzip, zstd예요. |
VARCHAR |
auto |
dateformat |
날짜를 파싱하고 쓸 때 사용하는 날짜 형식입니다. | VARCHAR |
(빈 값) |
date_format |
dateformat의 별칭이며 COPY 문에서만 사용할 수 있어요. |
VARCHAR |
(빈 값) |
decimal_separator |
숫자의 소수 구분자입니다. | VARCHAR |
. |
delim |
각 줄 안에서 컬럼을 구분하는 데 쓰는 구분 문자입니다 (예: , ; \t). 구분 문자는 최대 4바이트까지 가능해요 (예: 🦆). sep의 별칭입니다. |
VARCHAR |
, |
delimiter |
delim의 별칭이며 COPY 문에서만 사용할 수 있어요. |
VARCHAR |
, |
escape |
따옴표로 감싼 값 안에서 quote 문자를 이스케이프하는 데 쓰는 문자열입니다. |
VARCHAR |
" |
encoding |
CSV 파일이 사용하는 인코딩입니다. 옵션은 utf-8, utf-16, latin-1이에요. COPY 문(항상 utf-8 사용)에서는 사용할 수 없어요. |
VARCHAR |
utf-8 |
filename |
각 행에 해당 파일의 경로를 filename이라는 문자열 컬럼으로 추가합니다. read_csv에 전달된 경로나 글로브(glob) 패턴에 따라 상대 경로나 절대 경로가 반환되며, 파일 이름만 반환되지는 않아요. DuckDB v1.3.0부터 filename 컬럼은 가상 컬럼으로 자동 추가되며, 이 옵션은 호환성을 위해 유지됩니다. |
BOOL |
false |
files_to_sniff |
여러 파일을 읽을 때 CSV 스니퍼가 스키마를 감지하는 데 사용하는 파일 수입니다. 모든 파일을 스니핑하려면 -1로 설정하세요. |
BIGINT |
10 |
force_not_null |
지정한 컬럼의 값을 NULL 문자열과 대조하지 않습니다. NULL 문자열이 비어 있는 기본 경우에는, 빈 값이 NULL이 아니라 길이 0의 문자열로 읽힌다는 뜻이에요. |
VARCHAR[] |
[] |
header |
각 파일의 첫 줄에 컬럼 이름이 들어 있습니다. | BOOL |
false |
hive_partitioning |
경로를 Hive 파티셔닝 경로로 해석합니다. | BOOL |
(자동 감지) |
ignore_errors |
마주치는 파싱 오류를 무시합니다. | BOOL |
false |
max_line_size 또는 maximum_line_size. COPY 문에서는 사용할 수 없어요. |
최대 줄 길이(바이트)입니다. | BIGINT |
2000000 |
names 또는 column_names |
컬럼 이름 목록입니다. 예시를 참고하세요. | VARCHAR[] |
(빈 값) |
new_line |
줄바꿈 문자입니다. 옵션은 '\r', '\n', '\r\n'이에요. CSV 파서는 한 문자짜리와 두 문자짜리 줄 구분자만 구분합니다. 따라서 '\r'과 '\n'을 구분하지 않아요. |
VARCHAR |
(빈 값) |
normalize_names |
컬럼 이름을 정규화합니다. 이름에서 영숫자가 아닌 문자를 제거하고, 예약된 SQL 키워드인 컬럼 이름 앞에는 밑줄(_)을 붙입니다. |
BOOL |
false |
null_padding |
한 줄에 컬럼이 부족할 때 나머지 오른쪽 컬럼을 NULL 값으로 채웁니다. |
BOOL |
false |
nullstr 또는 null |
NULL 값을 나타내는 문자열들입니다. |
VARCHAR 또는 VARCHAR[] |
(빈 값) |
parallel |
병렬 CSV reader를 사용합니다. | BOOL |
true |
quote |
값을 따옴표로 감쌀 때 사용하는 문자열입니다. | VARCHAR |
" |
rejects_scan |
오류 있는 스캔 정보가 저장되는 임시 테이블의 이름입니다. | VARCHAR |
reject_scans |
rejects_table |
오류 있는 줄 정보가 저장되는 임시 테이블의 이름입니다. | VARCHAR |
reject_errors |
rejects_limit |
파일당 rejects 테이블에 기록되는 오류 줄 수의 상한입니다. 0으로 설정하면 제한이 적용되지 않아요. |
BIGINT |
0 |
sample_size |
파라미터 자동 감지를 위한 샘플 줄 수입니다. | BIGINT |
20480 |
sep |
각 줄 안에서 컬럼을 구분하는 데 쓰는 구분 문자입니다 (예: , ; \t). 구분 문자는 최대 4바이트까지 가능해요 (예: 🦆). delim의 별칭입니다. |
VARCHAR |
, |
skip |
각 파일 시작 부분에서 건너뛸 줄 수입니다. | BIGINT |
0 |
store_rejects |
오류가 있는 줄을 건너뛰고 rejects 테이블에 저장합니다. | BOOL |
false |
strict_mode |
CSV reader의 엄격함 수준을 강제합니다. true로 설정하면 문제를 만날 때마다 파서가 오류를 던져요. false로 설정하면 구조적으로 잘못된 파일을 읽으려고 시도합니다. 구조적으로 잘못된 파일을 읽으면 모호함이 생길 수 있으므로 이 옵션은 주의해서 써야 한다는 점을 꼭 기억하세요. |
BOOL |
true |
thousands |
숫자 값에서 천 단위 구분자를 식별하는 데 쓰는 문자입니다. 반드시 한 글자여야 하며 decimal_separator 옵션과 달라야 해요. |
VARCHAR |
(빈 값) |
timestampformat |
타임스탬프를 파싱하고 쓸 때 사용하는 타임스탬프 형식입니다. | VARCHAR |
(빈 값) |
timestamp_format |
timestampformat의 별칭이며 COPY 문에서만 사용할 수 있어요. |
VARCHAR |
(빈 값) |
types 또는 dtypes 또는 column_types |
컬럼 타입을 위치 기준 목록(리스트) 또는 이름 기준 구조체(struct)로 지정합니다. 예시를 참고하세요. | VARCHAR[] 또는 STRUCT |
(빈 값) |
union_by_name |
서로 다른 파일의 컬럼을 위치가 아닌 컬럼 이름 기준으로 정렬합니다. 이 옵션을 쓰면 메모리 사용이 늘어나요. | BOOL |
false |
팁 DuckDB의 CSV reader는
UTF-8(기본값),UTF-16,Latin-1인코딩을 지원해요. 다른 인코딩의 경우 encodings 확장을 사용하거나, 예를 들어iconv커맨드라인 도구로 변환할 수 있어요:iconv -f ISO-8859-2 -t UTF-8 input.csv > input-utf-8.csv
auto_type_candidates 상세
auto_type_candidates 옵션은 CSV reader가 컬럼 데이터 타입 감지에서 고려해야 할 데이터 타입을 지정하게 해 줍니다. 사용 예시:
SELECT * FROM read_csv('csv_file.csv', auto_type_candidates = ['BIGINT', 'DATE']);
auto_type_candidates 옵션의 기본값은 ['NULL', 'BOOLEAN', 'BIGINT', 'DOUBLE', 'TIME', 'DATE', 'TIMESTAMP', 'VARCHAR']입니다.
CSV 함수
read_csv는 CSV 스니퍼를 이용해 CSV reader의 올바른 설정을 자동으로 알아내고, 컬럼 타입도 자동으로 추론합니다. CSV 파일에 헤더가 있으면 그 헤더의 이름으로 컬럼 이름을 짓고, 헤더가 없다면 컬럼 이름이 column0, column1, column2, ...로 지정돼요. flights.csv 파일을 쓰는 예시를 볼게요:
SELECT * FROM read_csv('flights.csv');
| FlightDate | UniqueCarrier | OriginCityName | DestCityName |
|---|---|---|---|
| 1988-01-01 | AA | New York, NY | Los Angeles, CA |
| 1988-01-02 | AA | New York, NY | Los Angeles, CA |
| 1988-01-03 | AA | New York, NY | Los Angeles, CA |
경로는 현재 작업 디렉터리를 기준으로 한 상대 경로거나 절대 경로일 수 있어요.
read_csv로 영속 테이블을 만들 수도 있습니다:
CREATE TABLE ontime AS
SELECT * FROM read_csv('flights.csv');
DESCRIBE ontime;
| column_name | column_type | null | key | default | extra |
|---|---|---|---|---|---|
| FlightDate | DATE | YES | NULL | NULL | NULL |
| UniqueCarrier | VARCHAR | YES | NULL | NULL | NULL |
| OriginCityName | VARCHAR | YES | NULL | NULL | NULL |
| DestCityName | VARCHAR | YES | NULL | NULL | NULL |
SELECT * FROM read_csv('flights.csv', sample_size = 20_000);
delim/sep, quote, escape, header를 직접 지정하면 해당 파라미터의 자동 감지를 건너뛸 수 있어요:
SELECT * FROM read_csv('flights.csv', header = true);
glob이나 파일 목록을 제공하면 여러 파일을 한 번에 읽을 수 있습니다. 자세한 내용은 multiple files 섹션을 참고하세요.
COPY 문으로 쓰기
COPY 문으로 CSV 파일에서 테이블로 데이터를 불러올 수 있습니다. 이 문의 문법은 PostgreSQL에서 쓰는 것과 동일해요. COPY 문으로 데이터를 불러오려면 먼저 올바른 스키마(CSV 파일의 컬럼 순서와 일치하고, CSV 파일에 있는 값에 맞는 타입을 쓰는 스키마)를 가진 테이블을 만들어야 합니다. COPY는 CSV의 설정 옵션을 자동으로 감지해요.
CREATE TABLE ontime (
flightdate DATE,
uniquecarrier VARCHAR,
origincityname VARCHAR,
destcityname VARCHAR
);
COPY ontime FROM 'flights.csv';
SELECT * FROM ontime;
| flightdate | uniquecarrier | origincityname | destcityname |
|---|---|---|---|
| 1988-01-01 | AA | New York, NY | Los Angeles, CA |
| 1988-01-02 | AA | New York, NY | Los Angeles, CA |
| 1988-01-03 | AA | New York, NY | Los Angeles, CA |
CSV 형식을 직접 지정하고 싶다면 COPY의 설정 옵션으로 지정할 수 있어요.
CREATE TABLE ontime (flightdate DATE, uniquecarrier VARCHAR, origincityname VARCHAR, destcityname VARCHAR);
COPY ontime FROM 'flights.csv' (DELIMITER '|', HEADER);
SELECT * FROM ontime;
오류 있는 CSV 파일 읽기
DuckDB는 오류가 있는 CSV 파일도 읽을 수 있습니다. 자세한 내용은 Reading Faulty CSV Files 페이지를 참고하세요.
순서 보존
CSV reader는 preserve_insertion_order 설정 옵션에 따라 삽입 순서를 보존합니다. true(기본값)이면 CSV reader가 반환하는 결과 집합의 행 순서가 파일에서 읽은 해당 줄의 순서와 같아요. false면 순서가 보존된다는 보장이 없어요.
CSV 파일 쓰기
DuckDB는 COPY ... TO 문으로 CSV 파일을 쓸 수 있습니다.