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.csvnone). 옵션은 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_csvCSV 스니퍼를 이용해 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 파일을 쓸 수 있습니다.