CSV 데이터 가져오기
CSV 데이터 가져오기
CSV는 가장 흔한 데이터 교환 형식이면서도, 막상 읽어 보면 제각각인 파일이 많아요. DuckDB의 CSV 리더는 자동 감지로 대부분의 상황을 알아서 처리하고, 필요한 경우에는 옵션으로 정밀하게 조정할 수도 있어요. 여기서는 read_csv 함수와 COPY 구문을 이용해 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';
CSV 로딩
CSV 로딩, 즉 CSV 파일을 데이터베이스로 가져오는 일은 아주 흔하면서도 의외로 까다로운 작업이에요. CSV는 겉보기엔 단순해 보이지만, 들여다보면 불일치가 많아서 로딩을 어렵게 만드는 경우가 잦아요. CSV 파일은 종류가 다양하고, 종종 손상돼 있기도 하며, 스키마도 없는 경우가 많죠. CSV 리더는 이런 모든 상황을 감당할 수 있어야 해요.
DuckDB CSV 리더는 CSV 스니퍼를 이용해 파일을 분석한 뒤 어떤 설정 플래그를 쓸지 자동으로 추론해요. 대부분의 상황에서 이 방식이 올바르게 동작하므로 가장 먼저 시도할 옵션이에요. 아주 드물게 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 타입은 항상 폴백으로 포함돼요. 예시 참조 |
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에 전달한 경로나 글로브 패턴에 따라 상대·절대 경로가 반환돼요(파일 이름만이 아님). 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 |
컬럼 이름을 정규화해요. 영숫자가 아닌 문자를 제거하고, 예약어인 컬럼 이름 앞에는 밑줄(_)을 붙여요. |
BOOL |
false |
null_padding |
한 줄에 컬럼이 부족할 때 오른쪽 남은 컬럼을 NULL로 채워요. |
BOOL |
false |
nullstr 또는 null |
NULL 값을 나타내는 문자열 |
VARCHAR 또는 VARCHAR[] |
(비어 있음) |
parallel |
병렬 CSV 리더를 사용해요. | 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 리더의 엄격함 수준이에요. true면 파서가 문제를 만날 때 오류를 던지고, false면 구조적으로 잘못된 파일도 읽으려 시도해요. 구조가 잘못된 파일을 읽으면 모호함이 생길 수 있으니 주의해서 써야 해요. |
BOOL |
true |
thousands |
숫자 값에서 천 단위 구분자를 식별하는 문자예요. 한 글자여야 하고 decimal_separator 옵션과 달라야 해요. |
VARCHAR |
(비어 있음) |
timestampformat |
타임스탬프를 파싱·쓸 때 사용하는 타임스탬프 형식 | VARCHAR |
(비어 있음) |
timestamp_format |
timestampformat의 별칭이며 COPY 구문에서만 사용 가능해요. |
VARCHAR |
(비어 있음) |
types 또는 dtypes 또는 column_types |
컬럼 타입을 리스트(위치 기준) 또는 스트럭처(이름 기준)로 지정해요. 예시 참조 | VARCHAR[] 또는 STRUCT |
(비어 있음) |
union_by_name |
다른 파일의 컬럼을 위치 대신 컬럼 이름으로 정렬해요. 이 옵션을 쓰면 메모리 사용량이 늘어나요. | BOOL |
false |
DuckDB의 CSV 리더는
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 리더가 컬럼 데이터 타입 감지에서 고려할 데이터 타입을 지정해요. 사용 예시:
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 리더의 올바른 구성을 자동으로 알아내려 해요. 컬럼 타입도 자동으로 추론해요. 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);
글로브 패턴이나 파일 목록을 주면 여러 파일을 한 번에 읽을 수 있어요. 자세한 내용은 여러 파일 섹션을 참고하세요.
COPY 구문으로 쓰기
COPY 구문으로 CSV 파일의 데이터를 테이블로 로드할 수 있어요. 이 구문은 PostgreSQL에서 쓰는 것과 같은 문법이에요. COPY 구문으로 데이터를 로드하려면 먼저 올바른 스키마(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 파일도 읽을 수 있어요. 자세한 내용은 손상된 CSV 파일 읽기 페이지를 참고하세요.
순서 보존
CSV 리더는 순서 보존을 위해 preserve_insertion_order 설정 옵션을 존중해요.
true(기본값)면 CSV 리더가 반환하는 결과 집합의 행 순서가 파일에서 읽은 해당 줄 순서와 같아요.
false면 순서가 보존된다는 보장이 없어요.
CSV 파일 쓰기
DuckDB는 COPY ... TO 구문으로 CSV 파일을 쓸 수 있어요.
더 알아보기 (Learn more)
- CSV 자동 감지 — 스니퍼가 옵션을 추론하는 방식
- 레시피: CSV 파일 쓰기
- CSV 트러블슈팅 팁