PostgreSQL 2201G 오류 원인과 해결 방법 완벽 가이드

2201G
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() 함수는 lowhigh 값이 서로 달라야 정상적인 버킷 너비를 계산할 수 있습니다. 두 값이 같으면 버킷의 너비가 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): lowhigh가 같을 때 내부적으로 0으로 나누기가 발생하는 상황과 연관되어 있으며, PostgreSQL이 이를 2201G로 래핑하여 처리합니다.
  • 22P02 (invalid_text_representation): 문자열을 숫자형으로 캐스팅하는 과정에서 발생하며, width_bucket() 호출 전에 타입 변환 오류가 선행될 수 있습니다.
  • 2201E (invalid_argument_for_logarithm): 수학 함수류 에러로 2201G와 같은 카테고리(22 – Data Exception)에 속하며, 유사한 방어적 코딩 패턴으로 예방할 수 있습니다.

DBMS 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기