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

2201G
2026년 06월 07일 | DBMS Error 가이드

이 글에서 다루는 내용

2201G 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

2201G invalid argument for width bucket function 는?

PostgreSQL의 에러 코드 2201Gwidth_bucket() 함수에 유효하지 않은 인수가 전달되었을 때 발생하는 오류입니다. width_bucket() 함수는 주어진 값을 동일한 너비의 구간(버킷)으로 나누는 히스토그램 분석에 자주 사용되는 함수인데, 버킷의 수가 0 이하이거나 하한값과 상한값이 동일한 경우, 또는 입력값이 NaN(Not a Number)일 때 이 에러가 트리거됩니다. 데이터 분석, 통계 처리, 분포 시각화 등의 쿼리에서 자주 마주치는 에러이므로 원인과 해결 방법을 정확히 이해해두는 것이 중요합니다.


주요 발생 원인

1. 버킷 수(count)가 0 이하인 경우

width_bucket(operand, low, high, count) 함수에서 count 파라미터는 반드시 1 이상의 양의 정수여야 합니다. 실무에서는 동적으로 버킷 수를 계산하거나 사용자 입력값을 그대로 전달할 때 0 또는 음수가 들어오는 경우가 빈번하게 발생하며, 이때 PostgreSQL은 즉시 2201G 에러를 던집니다. 특히 집계 결과나 서브쿼리에서 도출된 값을 버킷 수로 사용할 때 이 문제가 자주 나타납니다.

2. 하한값(low)과 상한값(high)이 동일한 경우

width_bucket() 함수의 두 번째, 세 번째 인수인 lowhigh는 서로 달라야 하며 범위를 형성해야 합니다. 두 값이 동일하면 버킷의 너비가 0이 되어 수학적으로 구간을 정의할 수 없기 때문에 에러가 발생합니다. 실무에서는 데이터의 최솟값과 최댓값을 자동으로 구해서 범위로 사용하는 패턴이 많은데, 해당 컬럼의 모든 값이 동일할 경우 이 에러가 발생할 수 있습니다.

3. 입력값(operand)이 NaN인 경우

width_bucket() 함수는 operand(분류할 값) 자체가 NaN일 경우에도 2201G 에러를 발생시킵니다. NUMERIC 타입의 컬럼에는 NaN 값이 저장될 수 있으며, 외부 데이터 소스에서 가져온 데이터나 잘못된 계산 결과가 NaN으로 저장된 경우 이 에러가 트리거됩니다. FLOAT 또는 NUMERIC 타입을 다룰 때는 반드시 NaN 값 여부를 사전에 필터링해야 합니다.


해결 방법

원인 1 해결: 버킷 수 유효성 검사

버킷 수가 0 이하로 내려가지 않도록 GREATEST() 함수를 활용하거나, CASE 문으로 사전 검증하는 방법을 사용합니다.

-- 잘못된 쿼리: count가 0이 되어 에러 발생
SELECT width_bucket(score, 0, 100, 0)
FROM exam_results;

-- 해결 방법 1: GREATEST를 사용하여 최소 1 보장
SELECT width_bucket(score, 0, 100, GREATEST(1, bucket_count))
FROM exam_results
CROSS JOIN (SELECT COUNT(DISTINCT grade_level) AS bucket_count FROM exam_results) sub;

-- 해결 방법 2: CASE 문으로 버킷 수 검증
SELECT
  student_id,
  score,
  CASE
    WHEN :bucket_count <= 0 THEN NULL
    ELSE width_bucket(score, 0, 100, :bucket_count)
  END AS bucket
FROM exam_results;

-- 해결 방법 3: NULLIF 활용
SELECT width_bucket(score, 0, 100, NULLIF(GREATEST(bucket_count, 0), 0))
FROM exam_results
CROSS JOIN (SELECT 5 AS bucket_count) sub;

원인 2 해결: 하한값과 상한값이 동일한 경우

데이터의 최솟값과 최댓값을 동적으로 사용할 때는 두 값이 같은 경우를 반드시 처리해야 합니다.

-- 잘못된 쿼리: min = max인 경우 에러 발생
SELECT width_bucket(price, MIN(price), MAX(price), 10)
FROM products
GROUP BY category_id;

-- 해결 방법 1: low와 high가 같을 때 NULL 반환
SELECT
  product_id,
  price,
  CASE
    WHEN min_price = max_price THEN 1  -- 단일 버킷으로 처리
    ELSE width_bucket(price, min_price, max_price, 10)
  END AS price_bucket
FROM products
CROSS JOIN (
  SELECT MIN(price) AS min_price, MAX(price) AS max_price
  FROM products
) stats;

-- 해결 방법 2: 범위에 작은 오프셋 추가
SELECT
  product_id,
  width_bucket(
    price,
    min_price,
    CASE WHEN min_price = max_price THEN max_price + 1 ELSE max_price END,
    10
  ) AS price_bucket
FROM products
CROSS JOIN (
  SELECT MIN(price) AS min_price, MAX(price) AS max_price
  FROM products
) stats;

원인 3 해결: NaN 값 필터링

NaN 값이 포함된 컬럼을 처리할 때는 사전에 필터링하거나 대체값으로 치환합니다.

-- NaN이 포함된 데이터 확인
SELECT COUNT(*)
FROM measurements
WHERE value IS NOT NULL AND value::text = 'NaN';

-- 해결 방법 1: NaN 값을 쿼리에서 제외
SELECT
  sensor_id,
  width_bucket(value, 0.0, 100.0, 10) AS bucket
FROM measurements
WHERE value IS NOT NULL
  AND value::text != 'NaN'
  AND value != 'Infinity'::numeric;

-- 해결 방법 2: NaN을 NULL로 치환 후 처리
SELECT
  sensor_id,
  width_bucket(
    NULLIF(value::text, 'NaN')::numeric,
    0.0, 100.0, 10
  ) AS bucket
FROM measurements
WHERE value IS NOT NULL;

-- 해결 방법 3: 방어적 함수 래퍼 생성
CREATE OR REPLACE FUNCTION safe_width_bucket(
  operand NUMERIC,
  low NUMERIC,
  high NUMERIC,
  count INTEGER
) RETURNS INTEGER AS $$
BEGIN
  -- 모든 유효성 검사를 수행
  IF operand IS NULL OR operand::text = 'NaN' THEN
    RETURN NULL;
  END IF;
  IF low = high THEN
    RETURN 1;
  END IF;
  IF count <= 0 THEN
    RETURN NULL;
  END IF;
  RETURN width_bucket(operand, low, high, count);
EXCEPTION WHEN OTHERS THEN
  RETURN NULL;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- 안전한 함수 사용 예시
SELECT
  product_id,
  safe_width_bucket(price, 0, 10000, 20) AS price_bucket
FROM products;

예방 방법

1. 입력 데이터 검증 뷰(View) 또는 체크 제약 조건 활용

width_bucket()을 자주 사용하는 테이블이나 컬럼에는 사전에 데이터 품질을 보장하는 체크 제약 조건을 추가하고, 분석용 뷰에서 방어 로직을 포함시키는 것이 좋습니다.

-- 테이블 수준의 체크 제약 조건으로 NaN 방지
ALTER TABLE measurements
ADD CONSTRAINT chk_value_not_nan
CHECK (value::text != 'NaN' AND value != 'Infinity'::numeric);

-- 분석용 안전 뷰 생성
CREATE OR REPLACE VIEW v_safe_measurements AS
SELECT
  sensor_id,
  recorded_at,
  value,
  safe_width_bucket(value, 0.0, 1000.0, 20) AS distribution_bucket
FROM measurements
WHERE value IS NOT NULL
  AND value::text NOT IN ('NaN', 'Infinity', '-Infinity');

2. 동적 버킷 생성 시 매개변수 래핑 함수 표준화

프로젝트 내에서 width_bucket()을 사용하는 모든 쿼리에 대해 위에서 만든 safe_width_bucket() 같은 래퍼 함수를 표준으로 지정하고 팀 코딩 컨벤션에 포함시킵니다. CI/CD 파이프라인에 정적 분석 도구를 연동하여 width_bucket() 직접 호출을 감지하고 경고를 발생시키는 규칙을 추가하면 에러를 사전에 예방할 수 있습니다.


관련 에러

  • 2201F (invalid argument for logarithm): 로그 함수에 0 이하의 값이 입력될 때 발생하는 에러로, 수학 함수의 인수 유효성과 관련된 에러 패밀리에 속합니다.
  • 2201E (invalid argument for power function): POWER() 함수에 유효하지 않은 조합의 인수가 전달될 때 발생하며, 수치 연산 관련 에러 클래스 22(Data Exception)의 하위 에러입니다.
  • 22003 (numeric value out of range): 계산 결과가 해당 데이터 타입의 범위를 초과할 때 발생하며, width_bucket() 사용 시 경계값 설정이 잘못된 경우 함께 발생할 수 있습니다.
  • 22012 (division by zero): width_bucket() 내부적으로 버킷 너비를 계산할 때 나눗셈을 수행하므로, 구간 너비가 0인 경우 이 에러와 연관될 수 있습니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기