CSV·JSON 수집
CSV 자동 추론, 명시 스키마, JSON 추출과 반정형 데이터 수집의 실전 기준을 정리합니다.
핵심 요약
- CSV는 DESCRIBE SELECT로 먼저 추론 결과를 확인한다. 자동 추론은 시작점일 뿐이라, 운영에서는 read_csv의 columns로 컬럼명과 타입 계약을 고정한다.
- 품질 게이트는 잘못된 파일을 막는 장치가 아니라 빠르게 드러내는 장치다. row count, key uniqueness, 날짜·금액 범위만큼은 확인한다.
- JSON extension은 자동으로 로드된다. tabular가 아니므로 read_json의 columns로 되도록 빨리 분석 스키마로 펼치고, JSON indexing이 0-based라는 점을 문서에 남긴다.
- 반복 분석에 들어가는 CSV/JSON은 타입 고정과 품질 검사를 함께 한 뒤 COPY ... (FORMAT parquet)로 한 번 정리한다.
CSV와 JSON은 분석가에게 가장 자주 도착하는 파일이지만, 데이터베이스 관점에서는 가장 불안정한 입력입니다. DuckDB는 이런 파일을 바로 SQL로 검사한 뒤 명시 스키마와 품질 규칙으로 고정할 수 있습니다.
CSV 수집 원칙
| 단계 | 목적 | DuckDB 패턴 |
|---|---|---|
| 1차 탐색 | delimiter, header, type 추론 확인 | FROM 'file.csv', DESCRIBE SELECT |
| 계약 고정 | 컬럼명과 타입 확정 | read_csv(..., columns = {...}) |
| 품질 검사 | null, duplicate, domain 확인 | SQL aggregate, anti join |
| 저장 | 반복 분석용 columnar output | COPY ... TO ... (FORMAT parquet) |
DESCRIBE SELECT *
FROM 'raw/orders.csv';
CREATE OR REPLACE TABLE stage_orders AS
SELECT *
FROM read_csv(
'raw/orders.csv',
header = true,
columns = {
'order_id': 'VARCHAR',
'customer_id': 'VARCHAR',
'ordered_at': 'TIMESTAMP',
'amount': 'DECIMAL(18,2)'
}
);자동 추론은 시작점이지 계약이 아닙니다. 파일 provider가 바뀌거나 빈 파일이 들어와도 type inference가 흔들리지 않게, 운영 파이프라인에는 명시 스키마를 둡니다.
CSV 품질 게이트
SELECT
count(*) AS rows,
count(*) FILTER (WHERE order_id IS NULL) AS missing_order_id,
count(*) - count(DISTINCT order_id) AS duplicate_order_id,
min(ordered_at) AS min_ordered_at,
max(ordered_at) AS max_ordered_at
FROM stage_orders;품질 게이트는 "잘못된 파일을 읽지 못하게 하는 장치"라기보다 "잘못 읽힌 파일을 빠르게 드러내는 장치"입니다. CSV reader가 읽어낸 결과를 곧바로 믿지 말고, 최소한 row count, key uniqueness, 날짜 범위, 금액 범위는 확인합니다.
JSON 수집
DuckDB의 JSON extension은 대부분 배포에 포함되고 필요할 때 자동으로 로드됩니다. JSON은 tabular format이 아니라서, 운영 분석에서는 되도록 빨리 분석 스키마로 펼칩니다.
SELECT *
FROM read_json(
'raw/events.json',
format = 'array',
columns = {
user_id: 'VARCHAR',
event_name: 'VARCHAR',
event_time: 'TIMESTAMP',
properties: 'JSON'
}
);JSONPath와 JSON Pointer는 섞지 않는 게 좋습니다. 한 프로젝트 안에서는 하나를 표준으로 잡고, JSON indexing이 0-based라는 점을 문서에 남깁니다.
SELECT
user_id,
event_name,
properties->>'$.plan' AS plan,
try_cast(properties->>'$.amount' AS DECIMAL(18,2)) AS amount
FROM raw_events;CSV/JSON에서 Parquet로
반복 분석에 들어가는 CSV/JSON은 Parquet로 한 번 정리합니다. 단순 변환에 그치지 말고 타입 고정과 품질 검사를 같이 합니다.
COPY (
SELECT
order_id,
customer_id,
ordered_at::DATE AS order_date,
amount
FROM stage_orders
WHERE order_id IS NOT NULL
) TO 'curated/orders.parquet' (FORMAT parquet, COMPRESSION zstd);