Parquet 파일 읽고 쓰기

Parquet 파일 읽고 쓰기

Parquet은 압축된 컬럼 기반(columnar) 파일 형식이라 로드·처리 효율이 좋아요. DuckDB는 Parquet 읽기·쓰기를 효율적으로 지원할 뿐 아니라, Parquet 파일 스캔에 필터(filters)와 프로젝션(projections)을 밀어 넣는 것도 지원해요. 기본 예시부터 read_parquet 함수, 메타데이터 조회, 그리고 COPY로 쓰는 방법까지 정리해 볼게요.

출처: 공식문서

예시

Parquet 파일 하나 읽기:

SELECT * FROM 'test.parquet';

Parquet 파일에 어떤 컬럼/타입이 있는지 알아보기:

DESCRIBE SELECT * FROM 'test.parquet';

Parquet 파일에서 테이블 만들기:

CREATE TABLE test AS
    SELECT * FROM 'test.parquet';

파일이 .parquet로 끝나지 않으면 read_parquet 함수를 써요:

SELECT *
FROM read_parquet('test.parq');

리스트 파라미터로 Parquet 파일 세 개를 읽어 하나의 테이블처럼 취급하기:

SELECT *
FROM read_parquet(['file1.parquet', 'file2.parquet', 'file3.parquet']);

글로브 패턴에 맞는 모든 파일 읽기:

SELECT *
FROM 'test/*.parquet';

글로브 패턴에 맞는 모든 파일을 읽으면서, 각 행이 어느 파일에서 왔는지 알려주는 filename 가상 컬럼도 포함하기 (이 컬럼은 DuckDB v1.3.0부터 설정 옵션 없이 기본으로 제공돼요):

SELECT *, filename
FROM read_parquet('test/*.parquet');

글로브 리스트로 두 특정 폴더의 모든 Parquet 파일 읽기:

SELECT *
FROM read_parquet(['folder1/*.parquet', 'folder2/*.parquet']);

HTTPS로 읽기:

SELECT *
FROM read_parquet('https://some.url/some_file.parquet');

Parquet 파일의 메타데이터 조회:

SELECT *
FROM parquet_metadata('test.parquet');

Parquet 파일의 파일 메타데이터 조회:

SELECT *
FROM parquet_file_metadata('test.parquet');

Parquet 파일의 키-값 메타데이터 조회:

SELECT *
FROM parquet_kv_metadata('test.parquet');

Parquet 파일의 스키마 조회:

SELECT *
FROM parquet_schema('test.parquet');

쿼리 결과를 기본 압축(Snappy)으로 Parquet 파일에 쓰기:

COPY
    (SELECT * FROM tbl)
    TO 'result-snappy.parquet'
    (FORMAT parquet);

쿼리 결과를 특정 압축과 행 그룹 크기로 Parquet 파일에 쓰기:

COPY
    (FROM generate_series(100_000))
    TO 'test.parquet'
    (FORMAT parquet, COMPRESSION zstd, ROW_GROUP_SIZE 100_000);

데이터베이스 전체의 테이블 내용을 Parquet으로 내보내기:

EXPORT DATABASE 'target_directory' (FORMAT parquet);

Parquet 파일

Parquet 파일은 압축된 컬럼 기반 파일로, 로드·처리가 효율적이에요. DuckDB는 Parquet 파일을 효율적으로 읽고 쓰는 것을 모두 지원하며, Parquet 파일 스캔에 필터와 프로젝션을 밀어 넣는 것도 지원해요.

Parquet 데이터셋은 파일 수, 개별 파일 크기, 사용된 압축 알고리즘, 행 그룹 크기 등에 따라 달라져요. 이런 요소들은 성능에 큰 영향을 줘요. 자세한 내용은 성능 가이드를 참고하세요.

read_parquet 함수

함수 설명 예시
read_parquet(path_or_list_of_paths) Parquet 파일(들) 읽기 SELECT * FROM read_parquet('test.parquet');
parquet_scan(path_or_list_of_paths) read_parquet의 별칭 SELECT * FROM parquet_scan('test.parquet');

파일이 .parquet로 끝나면 함수 문법은 선택 사항이에요. 시스템이 Parquet 파일을 읽는 중임을 자동으로 추론해요:

SELECT * FROM 'test.parquet';

글로브 패턴이나 파일 목록을 주면 여러 파일을 한 번에 읽을 수 있어요. 자세한 내용은 여러 파일 섹션을 참고하세요.

파라미터

read_parquet 함수나 COPY 구문에 전달할 수 있는 옵션들이 여럿 있어요.

이름 설명 타입 기본값
binary_as_string 레거시 writer가 만든 Parquet 파일은 문자열의 UTF8 플래그를 제대로 설정하지 않아 문자열 컬럼이 BLOB으로 로드될 수 있어요. 이 값을 true로 설정하면 이진 컬럼을 문자열로 로드해요. BOOL false
can_have_nan FLOATDOUBLE 컬럼에 NaN 값이 들어갈 수 있는지 여부예요. true로 설정하면 필터 푸시다운에 컬럼의 min/max 통계를 쓸 때 NaN을 고려해요. NaN은 순서 비교가 되지 않기 때문이에요. BOOL false
encryption_config Parquet 암호화 설정 STRUCT -
filename 결과에 추가 filename 컬럼을 포함할지 여부예요. DuckDB v1.3.0부터 filename 컬럼이 가상 컬럼으로 자동 추가되어 이 옵션은 호환성 목적으로만 남아 있어요. BOOL false
file_row_number file_row_number 컬럼을 포함할지 여부 BOOL false
hive_partitioning 경로를 Hive 파티셔닝 경로로 해석할지 여부 BOOL (자동 감지)
union_by_name 여러 스키마의 컬럼을 위치가 아닌 이름으로 통일할지 여부 BOOL false
schema Parquet 파일을 지정된 스키마를 가진 것처럼 읽을 수 있게 해요. 필드 ID가 필요해요. MAP NULL

schema 파라미터 사용하기

schema 파라미터로 Parquet 파일을 특정 스키마로 읽을 수 있어요. 읽을 때 컬럼을 이름 바꾸기, 추가하기, 삭제하기, 재정렬하기, 또는 캐스팅하는 데 유용해요.

schema 파라미터를 쓰려면 필드 ID가 필요해요. DuckDB로 Parquet을 만들 때 필드 ID를 제공하려면:

COPY (SELECT 42::INTEGER AS i) TO 'integers.parquet' (FIELD_IDS {i: 0});

Parquet 파일 읽기:

SELECT *
FROM read_parquet('integers.parquet', schema = MAP {
                    0: {name: 'renamed_i', type: 'BIGINT', default_value: NULL},
                    1: {name: 'new_column', type: 'UTINYINT', default_value: 43}
                  });
┌───────────┬────────────┐
│ renamed_i │ new_column │
│   int64   │   uint8    │
├───────────┼────────────┤
│        42 │         43 │
└───────────┴────────────┘

schema 파라미터는 union_by_name = true와 함께 사용할 수 없어요.

부분 읽기

DuckDB는 Parquet 파일 자체에 프로젝션 푸시다운을 지원해요. 즉, Parquet 파일을 쿼리할 때 쿼리에 필요한 컬럼만 읽어요. 그래서 관심 있는 Parquet 부분만 읽을 수 있고, 이는 DuckDB가 자동으로 처리해요.

DuckDB는 Parquet 리더에 필터 푸시다운도 지원해요. Parquet 파일에서 스캔되는 컬럼에 필터를 적용하면 필터가 스캔 안으로 밀려 들어가고, 내장 zonemap을 이용해 파일 일부를 건너뛰는 데 쓰일 수도 있어요. 이는 Parquet 파일에 zonemap이 들어 있는지 여부에 달려 있어요.

필터·프로젝션 푸시다운은 상당한 성능 이점을 제공해요. 자세한 내용은 블로그 포스트 "Querying Parquet with Precision Using DuckDB"를 참고하세요.

삽입과 뷰

Parquet 파일의 데이터를 테이블에 삽입하거나 Parquet 파일에서 테이블을 직접 만들 수도 있어요. 이렇게 하면 Parquet 파일에서 데이터를 로드해 데이터베이스에 삽입해요:

Parquet 파일의 데이터를 테이블에 삽입하기:

INSERT INTO people
    SELECT * FROM read_parquet('test.parquet');

Parquet 파일에서 테이블 직접 만들기:

CREATE TABLE people AS
    SELECT * FROM read_parquet('test.parquet');

데이터를 Parquet 파일 안에 그대로 두고 Parquet 파일을 직접 쿼리하고 싶다면, read_parquet 함수 위에 뷰를 만들면 돼요. 그러면 Parquet 파일을 내장 테이블처럼 쿼리할 수 있어요:

Parquet 파일 위에 뷰 만들기:

CREATE VIEW people AS
    SELECT * FROM read_parquet('test.parquet');

Parquet 파일 쿼리하기:

SELECT * FROM people;

Parquet 파일 쓰기

DuckDB는 COPY 구문 문법으로 Parquet 파일 쓰기도 지원해요. COPY 구문의 모든 가능한 파라미터를 포함한 자세한 내용은 COPY 구문 페이지를 참고하세요.

쿼리를 Snappy 압축 Parquet 파일로 쓰기:

COPY
    (SELECT * FROM tbl)
    TO 'result-snappy.parquet'
    (FORMAT parquet);

tbl을 zstd 압축 Parquet 파일로 쓰기:

COPY tbl
    TO 'result-zstd.parquet'
    (FORMAT parquet, COMPRESSION zstd);

tbl을 가장 낮은 압축 수준(가장 빠른 압축)의 zstd Parquet 파일로 쓰기:

COPY tbl
    TO 'result-zstd.parquet'
    (FORMAT parquet, COMPRESSION zstd, COMPRESSION_LEVEL 1);

키-값 메타데이터와 함께 Parquet 파일로 쓰기:

COPY (
    SELECT
        42 AS number,
        true AS is_even
) TO 'kv_metadata.parquet' (
    FORMAT parquet,
    KV_METADATA {
        number: 'Answer to life, universe, and everything',
        is_even: 'not ''odd''' -- single quotes in values must be escaped
    }
);

Parquet v2 파일로 쓰기:

COPY tbl
    TO 'result-v2.parquet'
    (FORMAT parquet, PARQUET_VERSION 'V2');

CSV 파일을 압축 없는 Parquet 파일로 쓰기:

COPY
    'test.csv'
    TO 'result-uncompressed.parquet'
    (FORMAT parquet, COMPRESSION uncompressed);

쿼리를 zstd 압축과 행 그룹 크기로 Parquet 파일에 쓰기:

COPY
    (FROM generate_series(100_000))
    TO 'row-groups-zstd.parquet'
    (FORMAT parquet, COMPRESSION zstd, ROW_GROUP_SIZE 100_000);

데이터를 LZ4 압축 Parquet 파일로 쓰기:

COPY
    (FROM generate_series(100_000))
    TO 'result-lz4.parquet'
    (FORMAT parquet, COMPRESSION lz4);

또는 동일하게:

COPY
    (FROM generate_series(100_000))
    TO 'result-lz4.parquet'
    (FORMAT parquet, COMPRESSION lz4_raw);

데이터를 Brotli 압축 Parquet 파일로 쓰기:

COPY
    (FROM generate_series(100_000))
    TO 'result-brotli.parquet'
    (FORMAT parquet, COMPRESSION brotli);

Parquet 파일 딕셔너리 페이지 크기를 구성하려면 STRING_DICTIONARY_PAGE_SIZE_LIMIT 옵션(기본값: 1 MB)을 써요:

COPY
    lineitem
    TO 'lineitem-with-custom-dictionary-size.parquet'
    (FORMAT parquet, STRING_DICTIONARY_PAGE_SIZE_LIMIT 100_000);

DuckDB의 EXPORT 명령으로 데이터베이스 전체를 Parquet 파일 시리즈로 내보낼 수 있어요. 자세한 내용은 EXPORT 구문” 페이지를 참고하세요:

데이터베이스 전체의 테이블 내용을 Parquet으로 내보내기:

EXPORT DATABASE 'target_directory' (FORMAT parquet);

암호화

DuckDB는 암호화된 Parquet 파일 읽기·쓰기를 지원해요.

지원되는 기능

지원되는 Parquet 기능 목록은 Parquet 문서의 "Implementation status" 페이지에서 확인할 수 있어요.

Parquet 확장 설치와 로딩

Parquet 파일 지원은 확장을 통해 활성화돼요. parquet 확장은 거의 모든 클라이언트에 번들되어 있어요. 다만 클라이언트가 parquet 확장을 번들하지 않으면 확장을 별도로 설치해야 해요:

INSTALL parquet;

더 알아보기 (Learn more)