2026년 08월 01일 | DBMS Error 가이드
이 글에서 다루는 내용
01003 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
01003 null value eliminated in set function 는?
PostgreSQL 에러 코드 01003은 엄밀히 말하면 에러(ERROR)가 아닌 경고(WARNING) 수준의 메시지로, SQLSTATE 01003에 해당합니다. 이 경고는 SUM(), AVG(), COUNT(), MAX(), MIN() 등의 집계 함수(Set Function)를 사용할 때 입력 값 중 NULL이 포함되어 있고, 해당 NULL 값이 계산에서 자동으로 제외되었음을 알려주는 신호입니다. SQL 표준에 따르면 집계 함수는 NULL 값을 묵시적으로 무시하는 동작이 정의되어 있으므로, PostgreSQL은 이를 사용자에게 알리기 위해 경고 메시지를 발생시킵니다. 실무에서는 이 경고를 그냥 넘기면 데이터 분석 결과의 왜곡, 잘못된 통계 산출, 비즈니스 의사결정 오류로 이어질 수 있기 때문에 반드시 원인을 파악하고 적절히 대응해야 합니다.
주요 발생 원인
1. 집계 함수 대상 컬럼에 NULL 값이 존재하는 경우
가장 흔한 원인으로, 테이블 설계 시 특정 컬럼에 NOT NULL 제약을 두지 않아 NULL 데이터가 삽입된 상태에서 집계 함수를 호출할 때 발생합니다. 예를 들어 sales_amount 컬럼에 일부 행이 NULL로 저장된 경우 SUM(sales_amount)를 실행하면 해당 NULL 행은 무시되고 경고가 발생합니다. 이는 단순한 입력 누락부터 ETL 파이프라인의 결함, 외부 시스템 연동 오류까지 다양한 원인으로 발생할 수 있습니다.
2. JOIN 이후 발생하는 NULL 값에 대한 집계
LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN 등을 사용할 때 매칭되지 않는 행의 컬럼 값은 자동으로 NULL이 됩니다. 이 상태에서 JOIN 결과에 대해 곧바로 집계 함수를 적용하면 의도치 않게 NULL 값이 포함된 채로 집계가 수행되어 경고가 발생합니다. 특히 복잡한 다중 JOIN 쿼리에서는 어느 JOIN 단계에서 NULL이 유입되었는지 파악하기 어렵기 때문에 주의가 필요합니다.
3. 서브쿼리 또는 CTE(Common Table Expression)에서 유입된 NULL
서브쿼리나 CTE에서 반환된 결과셋에 NULL이 포함된 경우, 이를 외부 쿼리에서 집계 함수로 처리할 때 경고가 발생합니다. 중간 단계에서 NULL이 어떻게 발생했는지 추적이 어렵고, 특히 CASE WHEN 표현식에서 ELSE 절을 생략하면 암묵적으로 NULL이 반환되어 문제가 심화됩니다. 이런 경우는 디버깅 난이도가 높아 실무에서 놓치기 쉽습니다.
해결 방법
원인 1 해결: COALESCE 또는 NULLIF를 활용한 NULL 처리
집계 함수를 호출하기 전에 COALESCE()를 사용하여 NULL 값을 기본값으로 대체하거나, 상황에 따라 해당 행 자체를 필터링하는 방법을 사용합니다.
-- 문제가 발생하는 쿼리 예시
SELECT AVG(sales_amount)
FROM orders;
-- WARNING: null value eliminated in set function
-- 해결 방법 1: COALESCE로 NULL을 0으로 대체
SELECT AVG(COALESCE(sales_amount, 0))
FROM orders;
-- 해결 방법 2: NULL 행을 WHERE 절로 명시적 제외
SELECT AVG(sales_amount)
FROM orders
WHERE sales_amount IS NOT NULL;
-- 해결 방법 3: FILTER 절을 사용한 집계 (PostgreSQL 9.4+)
SELECT AVG(sales_amount) FILTER (WHERE sales_amount IS NOT NULL)
FROM orders;
-- NULL 값 현황 사전 확인 쿼리
SELECT
COUNT(*) AS total_rows,
COUNT(sales_amount) AS non_null_rows,
COUNT(*) - COUNT(sales_amount) AS null_rows,
ROUND(
(COUNT(*) - COUNT(sales_amount))::NUMERIC / COUNT(*) * 100, 2
) AS null_percentage
FROM orders;
원인 2 해결: JOIN 결과의 NULL 명시적 처리
-- 문제가 발생하는 쿼리: LEFT JOIN 이후 바로 집계
SELECT
c.customer_name,
SUM(o.order_amount) AS total_amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_name;
-- WARNING: null value eliminated in set function
-- 해결 방법: COALESCE로 JOIN 시 NULL을 명시적으로 처리
SELECT
c.customer_name,
SUM(COALESCE(o.order_amount, 0)) AS total_amount
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_name;
-- 또는 주문이 없는 고객 제외
SELECT
c.customer_name,
SUM(o.order_amount) AS total_amount
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_name;
원인 3 해결: CTE 및 서브쿼리에서의 NULL 방어 처리
-- 문제가 발생하는 CTE 예시
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
CASE
WHEN status = 'completed' THEN amount
-- ELSE 생략 시 NULL 반환됨
END AS valid_amount
FROM orders
)
SELECT
month,
SUM(valid_amount) AS total -- WARNING 발생
FROM monthly_sales
GROUP BY month;
-- 해결 방법: CASE WHEN에 ELSE 절 명시
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
CASE
WHEN status = 'completed' THEN amount
ELSE 0 -- 명시적 기본값 설정
END AS valid_amount
FROM orders
)
SELECT
month,
SUM(valid_amount) AS total
FROM monthly_sales
GROUP BY month;
-- NULL 유입 경로 추적을 위한 중간 결과 확인 방법
WITH monthly_sales AS (
SELECT
DATE_TRUNC('month', order_date) AS month,
CASE
WHEN status = 'completed' THEN amount
ELSE 0
END AS valid_amount
FROM orders
)
SELECT
month,
COUNT(*) AS total_rows,
COUNT(valid_amount) AS non_null_count,
SUM(valid_amount) AS total
FROM monthly_sales
GROUP BY month
ORDER BY month;
예방 방법
1. 테이블 설계 단계에서 NOT NULL 제약 및 DEFAULT 값 적용
데이터베이스 스키마 설계 시 업무 규칙상 반드시 값이 존재해야 하는 컬럼에는 NOT NULL 제약 조건을 설정하고, 적절한 DEFAULT 값을 지정하는 것이 기본 중의 기본입니다. 이렇게 하면 NULL 데이터가 아예 저장되지 않으므로 집계 함수 경고 자체가 발생하지 않습니다.
-- 권장: 스키마 설계 시 NOT NULL + DEFAULT 명시
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL,
order_date TIMESTAMP NOT NULL DEFAULT NOW(),
amount NUMERIC(12, 2) NOT NULL DEFAULT 0.00,
status VARCHAR(20) NOT NULL DEFAULT 'pending'
);
-- 기존 테이블에 제약 추가
ALTER TABLE orders
ALTER COLUMN amount SET NOT NULL,
ALTER COLUMN amount SET DEFAULT 0.00;
2. 집계 쿼리 작성 시 코드 리뷰 체크리스트 운영
팀 내 SQL 코드 리뷰 프로세스에 “집계 함수 사용 시 대상 컬럼의 NULL 가능 여부 확인” 항목을 반드시 포함시키고, 집계 함수에는 습관적으로 COALESCE()나 FILTER 절을 적용하는 코딩 컨벤션을 수립하는 것이 좋습니다. 또한 pg_stats 뷰를 활용하여 컬럼별 NULL 비율(null_frac)을 사전에 모니터링하면 문제를 조기에 발견할 수 있습니다.
-- pg_stats를 활용한 컬럼별 NULL 비율 모니터링
SELECT
schemaname,
tablename,
attname AS column_name,
null_frac AS null_fraction,
ROUND(null_frac * 100, 2) AS null_percentage
FROM pg_stats
WHERE tablename = 'orders'
AND null_frac > 0
ORDER BY null_frac DESC;
관련 에러
- 22003 (numeric_value_out_of_range): NULL 처리를 위해 COALESCE로 기본값을 설정할 때 데이터 타입 범위를 초과하면 함께 발생할 수 있습니다.
- 22012 (division_by_zero): NULL 제거 후 집계 결과로 나누기 연산을 수행할 때, 모든 값이 NULL이어서 COUNT가 0이 되는 상황에서 발생할 수 있습니다.
- 42803 (grouping_error): GROUP BY 절과 SELECT 절의 컬럼 불일치로 발생하며, NULL 집계 경고와 함께 복합적으로 나타나는 경우가 있습니다.
- 22007 (invalid_datetime_format): 날짜 컬럼에 NULL이 포함된 채로 DATE_TRUNC 등을 사용한 집계를 수행할 때 관련하여 발생할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.