ERP에서 거래처의 배송 지시사항을 CSV로 받아 일괄 갱신하는 설명용 업무 장면입니다. 담당자가 미리보기에서 빈칸 두 개를 확인합니다. 하나는 이번 파일에 지시사항이 전달되지 않은 행이고, 다른 하나는 기존 지시사항을 비워 달라는 행입니다. 두 행을 같은 값으로 적재하면 기존 지시사항을 유지해야 할 거래처까지 갱신될 수 있습니다.

PostgreSQL 18에서 COPY FROM으로 text 열에 CSV를 적재하는 경우를 기준으로, 파일의 표기와 DB에 저장된 값, 실제 갱신 동작을 차례로 구분합니다.
CSV 원본에서 따옴표와 공백 확인
미리보기에서 빈 셀을 발견하면 원본 파일을 텍스트로 열어 해당 행을 확인합니다. CSV 문법 자체에는 SQL NULL을 표현하는 공통 규칙이 없습니다. PostgreSQL은 FORMAT csv의 기본 설정에서 따옴표 없는 빈 필드를 NULL로, 큰따옴표 두 개로 표시한 ""를 길이 0의 문자열로 읽습니다. 공백 한 칸은 text 열에 길이 1의 값으로 남습니다. PostgreSQL 18 — COPY의 CSV 형식과 옵션 · RFC 4180 — CSV 필드의 따옴표와 공백 규칙
| 원본 CSV 행 | delivery_note 적재값 |
판별 조건 |
|---|---|---|
V001, |
NULL |
delivery_note IS NULL |
V002,"" |
빈 문자열 '' |
delivery_note = '' |
V003," " |
공백 한 칸 | delivery_note = ' ' |
V004,경비실에 맡김 |
경비실에 맡김 |
delivery_note = '경비실에 맡김' |
표의 V001,에는 쉼표 뒤에 값이 비어 있는 두 번째 필드가 있습니다. 쉼표까지 빠진 V001은 필드가 하나뿐이어서 두 열을 기대하는 적재에서 입력 오류가 납니다. 공급처가 NULL '__MISSING__'처럼 별도 표식을 쓰기로 했다면, 따옴표 없는 __MISSING__이 NULL이 되고 따옴표 없는 빈 필드는 빈 문자열로 읽힙니다. FORCE_NULL과 FORCE_NOT_NULL을 쓰는 열도 기본 규칙과 해석이 달라지므로 실제 배치 명령을 확인합니다. PostgreSQL 18 — COPY의 CSV 형식과 옵션

배송 지시사항의 유지·삭제·변경 기준
같은 NULL도 파일의 용도에 따라 의미가 달라집니다. 일부 필드만 갱신하는 파일이라면 공급처와 NULL은 기존 값 유지, 빈 문자열은 지시사항 삭제, 그 외 텍스트는 새 값으로 교체한다고 합의할 수 있습니다. 이 계약에서는 공백만 있는 값은 보류하고 공급처에 확인하는 편이 명확합니다.
반대로 현재 상태 전체를 제공하는 파일에서 NULL은 원본에 값이 없음을 뜻할 수 있습니다. 이때 NULL을 일괄적으로 기존 값 유지로 처리하면 이미 사라진 지시사항이 남을 수 있습니다. 공급처가 누락과 삭제를 같은 표기로 내보낸다면 적재 단계에서 의미를 복원할 수 없으므로 delivery_note_action 같은 열로 KEEP, CLEAR, SET을 구분하는 방법을 협의합니다.
임시 테이블에서 네 가지 입력 검증
아래 예제를 psql에서 같은 세션으로 실행하면 운영 테이블을 건드리지 않고 기본 CSV 규칙을 확인할 수 있습니다. COPY FROM STDIN 다음의 네 줄이 적재 데이터이고, \.은 psql에서 데이터 입력의 끝을 표시합니다. NULL은 IS NULL로, 빈 문자열은 = ''로 판별합니다. PostgreSQL 18 — psql과 인라인 COPY 데이터 · PostgreSQL 18 — 비교 연산자와 IS NULL
CREATE TEMP TABLE p045_delivery_stage (
vendor_code text,
delivery_note text
);
COPY p045_delivery_stage (vendor_code, delivery_note)
FROM STDIN WITH (FORMAT csv);
V001,
V002,""
V003," "
V004,경비실에 맡김
\.
SELECT vendor_code,
CASE
WHEN delivery_note IS NULL THEN 'missing'
WHEN delivery_note = '' THEN 'empty'
WHEN btrim(delivery_note) = '' THEN 'spaces_only'
ELSE 'text'
END AS value_state
FROM p045_delivery_stage
ORDER BY vendor_code;
예상 결과는 V001부터 missing, empty, spaces_only, text 순서입니다. 이 예제에서 btrim은 문자열 양끝의 일반 공백을 제거해 분류에만 사용합니다. 탭이나 줄바꿈까지 같은 유형으로 처리할지는 별도로 정합니다. PostgreSQL 18 — 문자열 함수 btrim
실제 배포 전에는 공급처 파일과 운영 배치의 COPY 옵션으로 같은 검사를 합니다. 일부 갱신 계약을 적용한다면 기존 지시사항이 있는 테스트 행으로 갱신 미리보기를 만들어 V001은 유지, V002는 삭제, V003은 보류, V004는 새 값 반영으로 분류되는지 확인합니다.

결론
먼저 공급처에 실제 파일을 받아 빈칸의 원본 표기를 확인합니다. 그다음 파일이 전체 상태를 전달하는지, 바뀐 항목만 전달하는지에 맞춰 유지·삭제·변경 규칙을 정합니다. 운영 반영 여부는 임시 테이블의 분류 결과와 갱신 미리보기에서 두 빈칸이 합의한 대로 처리되는지 확인한 뒤 결정합니다.