2026년 08월 11일 | DBMS Error 가이드
이 글에서 다루는 내용
2201G 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
2201G invalid argument for width bucket function 는?
PostgreSQL 에러 코드 2201G는 width_bucket() 함수에 유효하지 않은 인수(argument)가 전달되었을 때 발생하는 오류입니다. width_bucket() 함수는 주어진 값을 지정된 범위 내에서 동일한 너비의 버킷(bucket)으로 분류하는 함수인데, 이 함수가 정상적으로 동작하기 위한 전제 조건이 충족되지 않을 경우 이 에러가 발생합니다. 주로 버킷의 수가 0 이하이거나, 하한값과 상한값이 동일하거나, NaN(Not a Number) 또는 무한대(Infinity) 값이 입력될 때 나타납니다.
주요 발생 원인
- 버킷 수(count)가 0 이하인 경우
width_bucket(operand, low, high, count) 함수에서 count 파라미터는 반드시 1 이상의 양의 정수여야 합니다. 버킷 수가 0이거나 음수로 지정되면 PostgreSQL은 유효한 버킷 구조를 생성할 수 없기 때문에 즉시 2201G 에러를 발생시킵니다. 이는 동적으로 버킷 수를 계산하는 로직에서 특히 자주 발생하며, 계산 결과가 예상치 못하게 0 또는 음수가 되는 경우에 트리거됩니다.
- 하한값(low)과 상한값(high)이 동일한 경우
width_bucket() 함수는 low와 high 값이 서로 달라야 정상적인 버킷 너비를 계산할 수 있습니다. 두 값이 같으면 버킷의 너비가 0이 되어 수학적으로 나눗셈이 불가능해지므로 에러가 발생합니다. 이 상황은 실시간 데이터 파이프라인에서 데이터의 최솟값과 최댓값이 같은 단일 값 데이터셋을 처리할 때 자주 발생합니다.
- NaN 또는 Infinity 값이 입력된 경우
width_bucket() 함수의 operand, low, high 파라미터 중 어느 하나라도 NaN 또는 Infinity(-Infinity 포함) 값을 가지면 에러가 발생합니다. 이는 특히 외부 데이터 소스에서 불완전하거나 손상된 부동소수점 데이터를 불러올 때, 또는 집계 연산의 결과로 NaN이 생성될 때 문제가 됩니다.
해결 방법
원인 1 해결: 버킷 수가 0 이하인 경우
버킷 수가 항상 1 이상이 되도록 GREATEST() 함수나 CASE 구문으로 방어 로직을 추가합니다.
-- 문제가 되는 쿼리 예시
SELECT width_bucket(score, 0, 100, 0) -- count=0 이므로 에러 발생
FROM exam_results;
-- 해결 방법 1: GREATEST()를 사용해 최솟값 보장
SELECT width_bucket(score, 0, 100, GREATEST(1, bucket_count))
FROM exam_results;
-- 해결 방법 2: CASE 구문으로 방어적 처리
SELECT
score,
CASE
WHEN bucket_count <= 0 THEN NULL
ELSE width_bucket(score, 0, 100, bucket_count)
END AS bucket
FROM exam_results;
-- 해결 방법 3: 동적 버킷 수 계산 시 안전장치 추가
SELECT width_bucket(
sale_amount,
0,
1000000,
GREATEST(1, FLOOR(COUNT(*) OVER () / 10)::int)
)
FROM sales_data;
원인 2 해결: 하한값과 상한값이 동일한 경우
데이터의 최솟값과 최댓값이 같은 경우를 사전에 확인하고, 동일할 경우 대체 처리를 합니다.
-- 문제가 되는 쿼리 예시
SELECT width_bucket(value, 100, 100, 10) -- low=high 이므로 에러 발생
FROM measurements;
-- 해결 방법 1: NULLIF로 low=high인 경우 NULL 반환
SELECT
value,
CASE
WHEN MIN(value) OVER () = MAX(value) OVER () THEN 1 -- 단일 버킷으로 처리
ELSE width_bucket(
value,
MIN(value) OVER (),
MAX(value) OVER (),
10
)
END AS bucket
FROM measurements;
-- 해결 방법 2: 실제 데이터 범위를 기반으로 동적 처리
WITH stats AS (
SELECT
MIN(price) AS min_price,
MAX(price) AS max_price
FROM products
)
SELECT
p.product_id,
p.price,
CASE
WHEN s.min_price = s.max_price THEN 1
ELSE width_bucket(p.price, s.min_price, s.max_price, 5)
END AS price_bucket
FROM products p
CROSS JOIN stats s;
원인 3 해결: NaN 또는 Infinity 값이 입력된 경우
입력값에서 특수 부동소수점 값을 필터링하거나 대체합니다.
-- 문제가 되는 쿼리 예시
SELECT width_bucket('NaN'::float, 0, 100, 10); -- NaN으로 에러 발생
SELECT width_bucket(1.0/0, 0, 100, 10); -- Infinity로 에러 발생
-- 해결 방법 1: IS FINITE 조건으로 유효한 값만 처리
SELECT
sensor_id,
reading_value,
CASE
WHEN reading_value IS NULL THEN NULL
WHEN NOT isfinite(reading_value) THEN NULL -- NaN, Infinity 제외
ELSE width_bucket(reading_value, 0.0, 100.0, 10)
END AS bucket
FROM sensor_readings;
-- 해결 방법 2: NaN과 Infinity를 명시적으로 확인
SELECT
id,
measurement,
CASE
WHEN measurement != measurement THEN NULL -- NaN 확인 (NaN != NaN은 true)
WHEN measurement = 'Infinity'::float OR measurement = '-Infinity'::float THEN NULL
WHEN measurement BETWEEN 0 AND 1000 THEN
width_bucket(measurement, 0, 1000, 20)
ELSE NULL
END AS bucket
FROM sensor_data;
-- 해결 방법 3: 데이터 정제 후 처리하는 CTE 활용
WITH clean_data AS (
SELECT
id,
measurement
FROM raw_measurements
WHERE
measurement IS NOT NULL
AND isfinite(measurement)
AND measurement BETWEEN -1e10 AND 1e10
)
SELECT
id,
width_bucket(measurement, 0, 500, 10) AS bucket
FROM clean_data;
배열 버전 width_bucket() 사용 시 해결 방법
PostgreSQL은 정렬된 배열을 버킷 경계로 사용하는 오버로드 버전도 제공합니다. 이 버전에서는 배열이 비어있을 경우 에러가 발생합니다.
-- 문제: 빈 배열 전달
SELECT width_bucket(5.0, ARRAY[]::float[]); -- 에러 발생
-- 해결: 배열 유효성 검사 추가
SELECT
value,
CASE
WHEN array_length(thresholds, 1) IS NULL THEN NULL -- 빈 배열 처리
ELSE width_bucket(value, thresholds)
END AS bucket
FROM (
SELECT
75.5 AS value,
ARRAY[0, 25, 50, 75, 100]::float[] AS thresholds
) t;
예방 방법
- 입력 유효성 검사 함수(Wrapper Function) 생성
width_bucket() 를 직접 호출하는 대신, 모든 입력 조건을 사전에 검증하는 래퍼 함수를 만들어 프로젝트 전체에서 일관되게 사용하는 것이 좋습니다. 이렇게 하면 코드 중복을 줄이고, 에러 처리 로직을 한 곳에서 관리할 수 있어 유지보수성이 크게 향상됩니다.
“`sql
— 안전한 width_bucket 래퍼 함수 생성
CREATE OR REPLACE FUNCTION safe_width_bucket(
operand double precision,
low double precision,
high double precision,
count integer
)
RETURNS integer
LANGUAGE plpgsql
IMMUTABLE
AS $$
BEGIN
— 입력값 유효성 검사
IF operand IS NULL OR low IS NULL OR high IS NULL OR count IS NULL THEN
RETURN NULL;
END IF;
IF NOT isfinite(operand) OR NOT isfinite(low) OR NOT isfinite(high) THEN
RETURN NULL;
END IF;
IF count <= 0 THEN
RAISE WARNING ‘width_bucket count must be positive, got: %’, count;
RETURN NULL;
END IF;
IF low = high THEN
RAISE WARNING ‘width_bucket low and high must be different’;
RETURN 1; — 모든 값을 1번 버킷으로 분류
END IF;
RETURN width_bucket(operand, low, high, count);
END;
$$;
— 사용 예시
SELECT safe_width_bucket(score, 0, 100, 10)
FROM exam_results;
“`
- 데이터 파이프라인에 CHECK 제약 조건 및 모니터링 쿼리 추가
데이터가 테이블에 적재되는 시점에 CHECK 제약 조건을 추가하고, 정기적인 데이터 품질 모니터링 쿼리를 통해 문제가 될 수 있는 값을 사전에 탐지합니다. 이를 통해 분석 쿼리 실행 시점이 아닌 데이터 적재 시점에 문제를 차단할 수 있어 훨씬 효율적입니다.
“`sql
— 테이블에 CHECK 제약 조건 추가
ALTER TABLE sensor_readings
ADD CONSTRAINT chk_finite_value
CHECK (reading_value IS NULL OR isfinite(reading_value));
— 데이터 품질 모니터링 쿼리 (주기적으로 실행)
SELECT
COUNT(*) FILTER (WHERE NOT isfinite(measurement)) AS infinite_nan_count,
COUNT(*) FILTER (WHERE measurement IS NULL) AS null_count,
MIN(measurement) AS min_val,
MAX(measurement) AS max_val,
COUNT(DISTINCT measurement) AS distinct_count
FROM raw_measurements;
“`
관련 에러
- 22003 (numeric_value_out_of_range):
width_bucket()결과값이 integer 범위를 초과할 때 발생할 수 있으며, 버킷 수를 매우 크게 설정하거나 operand 값이 극단적으로 클 때 연계하여 나타날 수 있습니다. - 22012 (division_by_zero):
low와high가 같을 때 내부적으로 0으로 나누기가 발생하는 상황과 연관되어 있으며, PostgreSQL이 이를 2201G로 래핑하여 처리합니다. - 22P02 (invalid_text_representation): 문자열을 숫자형으로 캐스팅하는 과정에서 발생하며,
width_bucket()호출 전에 타입 변환 오류가 선행될 수 있습니다. - 2201E (invalid_argument_for_logarithm): 수학 함수류 에러로 2201G와 같은 카테고리(22 – Data Exception)에 속하며, 유사한 방어적 코딩 패턴으로 예방할 수 있습니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.