숏컷 코드
COPY staging_users (email, name, created_at)
FROM '/var/lib/postgresql/import/users.csv'
WITH (FORMAT csv, HEADER true);\copy staging_users (email, name, created_at)
FROM './users.csv'
WITH (FORMAT csv, HEADER true)문법
COPY는 서버 파일, \copy는 클라이언트 파일을 읽는다
COPY ... FROM '/path/file.csv'는 PostgreSQL 서버 프로세스가 접근할 수 있는 파일을 읽습니다. 그래서 파일 경로, 권한, 컨테이너 volume 위치가 서버 기준이어야 합니다. 반면 psql의 \copy는 클라이언트 쪽 파일을 읽어서 서버로 전송합니다.
-- 서버 파일 기준
COPY products
FROM '/var/lib/postgresql/import/products.csv'
WITH (FORMAT csv, HEADER true);# psql 클라이언트가 보는 로컬 파일 기준
\copy products FROM './products.csv' WITH (FORMAT csv, HEADER true)운영 배치나 컨테이너 환경에서는 이 차이가 중요합니다. 파일이 애플리케이션 서버에는 있어도 DB 서버에는 없으면 SQL COPY는 실패합니다.
staging table을 거치면 검증과 변환을 분리할 수 있다
대량 데이터를 바로 운영 테이블에 넣기보다 staging table에 먼저 적재하면 타입 변환, 중복 검사, 외래 키 검증, 누락값 보정 과정을 분리할 수 있습니다.
CREATE TEMP TABLE staging_users (
email TEXT,
name TEXT,
created_at TEXT
);
COPY staging_users
FROM '/tmp/users.csv'
WITH (FORMAT csv, HEADER true);
INSERT INTO users (email, name, created_at)
SELECT email, name, created_at::timestamptz
FROM staging_users
WHERE email IS NOT NULL;CSV의 날짜 형식이나 빈 문자열 정책이 애플리케이션 스키마와 다르면, staging 단계에서 명시적으로 변환하는 편이 오류 위치를 추적하기 쉽습니다.
운영 기준
| 상황 | 접근 |
|---|---|
| 서버 안의 파일을 DB가 직접 읽음 | COPY |
| 로컬 파일을 psql에서 올림 | \copy |
| 원본 데이터 품질이 불확실함 | staging table 후 검증 |
| 대량 적재 후 실행 계획이 흔들림 | ANALYZE 실행 |
| 중복 키가 섞일 수 있음 | staging 후 ON CONFLICT 처리 |
대량 적재 직후에는 테이블 통계가 실제 분포와 달라질 수 있습니다. 이후 조회 성능이 중요하면 ANALYZE target_table;로 통계를 갱신합니다.
주의할 점
COPY FROM은 많은 행을 빠르게 넣기 때문에 실패했을 때 영향 범위도 큽니다. 운영 테이블에 바로 넣기 전 staging table에서 행 수, NULL, 중복, 타입 변환 오류를 확인하세요.
CSV 옵션은 원본 형식과 정확히 맞아야 합니다. HEADER, delimiter, quote, escape 설정이 다르면 열이 밀리거나 문자열이 잘못 해석될 수 있습니다.
참고 링크
1 sources