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

22014
2026년 08월 10일 | DBMS Error 가이드

이 글에서 다루는 내용

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

22014 invalid argument for ntile function 는?

PostgreSQL 에러 코드 22014는 NTILE() 윈도우 함수에 잘못된 인자가 전달되었을 때 발생하는 오류입니다. NTILE(n) 함수는 결과 집합을 n개의 버킷(bucket)으로 균등하게 나누는 윈도우 함수인데, 여기서 n이 0이거나 음수, 또는 NULL 값일 경우 이 에러가 트리거됩니다. 주로 동적으로 생성된 파티션 수를 인자로 넘길 때, 혹은 서브쿼리나 변수를 통해 값을 전달할 때 예상치 못한 값이 들어오면서 발생하는 경우가 많습니다.


주요 발생 원인

  • NTILE 인자에 0 또는 음수 값이 전달되는 경우

NTILE() 함수의 인자 n은 반드시 양의 정수(1 이상)이어야 합니다. 하드코딩된 값이 아닌 계산식이나 변수를 통해 인자를 전달할 때, 데이터 상태에 따라 0이나 음수가 들어오는 경우가 실무에서 가장 빈번하게 발생합니다. 예를 들어 전체 행 수를 기반으로 버킷 수를 동적으로 계산하는 로직에서, 필터링 결과가 예상보다 적을 때 이런 상황이 발생할 수 있습니다.

“`sql

— 에러 발생 예시: n = 0

SELECT

employee_id,

salary,

NTILE(0) OVER (ORDER BY salary DESC) AS bucket

FROM employees;

— ERROR: argument of ntile must be greater than zero

— SQLSTATE: 22014

— 에러 발생 예시: n = -5

SELECT

employee_id,

salary,

NTILE(-5) OVER (ORDER BY salary DESC) AS bucket

FROM employees;

— ERROR: argument of ntile must be greater than zero

— SQLSTATE: 22014

“`

  • NULL 값이 NTILE 인자로 전달되는 경우

서브쿼리나 변수를 통해 NTILE의 인자를 동적으로 전달할 때, 해당 값이 NULL이면 에러가 발생합니다. 특히 설정 테이블이나 파라미터 테이블에서 버킷 수를 조회해 사용하는 패턴에서 해당 레코드가 없거나 값이 NULL인 경우 이 문제가 쉽게 재현됩니다. NULL 처리를 명시적으로 하지 않으면 운영 환경에서 예기치 않게 서비스 장애로 이어질 수 있습니다.

“`sql

— 에러 발생 예시: NULL 인자

DO $$

DECLARE

v_bucket_count INTEGER := NULL;

BEGIN

— 이 쿼리는 v_bucket_count가 NULL이므로 에러 발생

PERFORM NTILE(v_bucket_count) OVER (ORDER BY 1)

FROM generate_series(1, 10);

END;

$$;

— ERROR: argument of ntile must be greater than zero

— SQLSTATE: 22014

“`

  • 동적 SQL 또는 파라미터 바인딩 시 타입 불일치로 인한 잘못된 값 전달

애플리케이션 레이어에서 파라미터를 바인딩할 때 타입 변환 오류나 빈 문자열이 전달되어 예상치 못한 값이 NTILE에 전달되는 경우입니다. 특히 ORM(Object-Relational Mapping) 프레임워크를 사용할 때, 정수로 전달되어야 할 값이 잘못 캐스팅되거나 기본값 처리 없이 넘어오는 상황에서 발생합니다. 이는 개발 환경에서는 재현하기 어렵고 운영 환경에서만 나타나는 경향이 있어 디버깅이 까다롭습니다.

“`sql

— 동적 SQL에서 잘못된 값 전달 예시

DO $$

DECLARE

v_input TEXT := ”; — 빈 문자열이 들어온 경우

v_bucket INTEGER;

BEGIN

— 빈 문자열을 정수로 캐스팅하면 에러 발생

v_bucket := v_input::INTEGER; — 이미 여기서 에러 가능

— 혹은 0으로 변환된 경우 NTILE에서 에러 발생

END;

$$;

“`


해결 방법

원인 1 해결: GREATEST() 함수로 최솟값 보장

가장 간단하고 효과적인 방법은 GREATEST() 함수를 사용하여 NTILE에 전달되는 값이 항상 1 이상이 되도록 보장하는 것입니다.

-- GREATEST()를 활용한 안전한 NTILE 사용
SELECT
    employee_id,
    department_id,
    salary,
    NTILE(GREATEST(1, :bucket_count)) OVER (
        PARTITION BY department_id
        ORDER BY salary DESC
    ) AS salary_bucket
FROM employees;

-- 동적 버킷 수 계산 시 안전하게 처리
WITH bucket_calc AS (
    SELECT GREATEST(1, COUNT(*)::INTEGER / 10) AS bucket_count
    FROM employees
    WHERE department_id = 10
)
SELECT
    e.employee_id,
    e.salary,
    NTILE((SELECT bucket_count FROM bucket_calc)) OVER (
        ORDER BY e.salary DESC
    ) AS bucket
FROM employees e
WHERE e.department_id = 10;

원인 2 해결: COALESCE()로 NULL 방어

NULL이 들어올 가능성이 있는 경우 COALESCE()를 사용해 기본값으로 대체합니다.

-- COALESCE()를 통한 NULL 방어
SELECT
    employee_id,
    salary,
    NTILE(COALESCE(v_bucket_count, 4)) OVER (
        ORDER BY salary DESC
    ) AS bucket
FROM employees;

-- 설정 테이블에서 버킷 수를 조회할 때 NULL 방어
WITH config AS (
    SELECT COALESCE(
        (SELECT config_value::INTEGER
         FROM app_config
         WHERE config_key = 'salary_bucket_count'),
        4  -- 기본값 4
    ) AS bucket_count
)
SELECT
    e.employee_id,
    e.salary,
    NTILE((SELECT bucket_count FROM config)) OVER (
        ORDER BY e.salary DESC
    ) AS salary_quartile
FROM employees e;

원인 3 해결: 입력값 검증 후 NTILE 실행

PL/pgSQL 함수 내에서 인자 검증 로직을 추가하여 잘못된 값이 NTILE에 전달되지 않도록 방어합니다.

-- 안전한 NTILE 래퍼 함수 작성
CREATE OR REPLACE FUNCTION safe_ntile_query(p_bucket_count INTEGER)
RETURNS TABLE(employee_id INTEGER, salary NUMERIC, bucket INTEGER)
LANGUAGE plpgsql
AS $$
DECLARE
    v_safe_bucket INTEGER;
BEGIN
    -- 입력값 검증
    IF p_bucket_count IS NULL OR p_bucket_count <= 0 THEN
        RAISE WARNING 'Invalid bucket count: %. Using default value 4.', p_bucket_count;
        v_safe_bucket := 4;
    ELSE
        v_safe_bucket := p_bucket_count;
    END IF;

    RETURN QUERY
    SELECT
        e.employee_id,
        e.salary,
        NTILE(v_safe_bucket) OVER (ORDER BY e.salary DESC)::INTEGER AS bucket
    FROM employees e;
END;
$$;

-- 함수 호출 예시
SELECT * FROM safe_ntile_query(NULL);   -- 경고 후 기본값 4로 실행
SELECT * FROM safe_ntile_query(-1);     -- 경고 후 기본값 4로 실행
SELECT * FROM safe_ntile_query(10);     -- 정상 실행

예방 방법

  • 입력값 검증 레이어를 데이터베이스 함수에 포함시키기

NTILE()을 사용하는 모든 쿼리를 PL/pgSQL 함수로 캡슐화하고, 함수 내부에서 반드시 인자 유효성 검사를 수행하십시오. CHECK 제약 조건처럼 데이터베이스 레벨에서 잘못된 값이 함수에 도달하기 전에 차단하는 것이 가장 안전한 방법입니다. 특히 동적으로 버킷 수를 계산하는 로직에서는 GREATEST(1, COALESCE(계산식, 기본값)) 패턴을 표준으로 사용하는 것을 권장합니다.

“`sql

— 권장 패턴: GREATEST + COALESCE 조합

SELECT

employee_id,

salary,

NTILE(GREATEST(1, COALESCE(dynamic_bucket_count, 4))) OVER (

ORDER BY salary DESC

) AS bucket

FROM employees

CROSS JOIN (SELECT count_value AS dynamic_bucket_count FROM config_table LIMIT 1) cfg;

“`

  • 통합 테스트에 경계값(Boundary Value) 케이스 반드시 포함하기

NTILE() 함수를 사용하는 쿼리에 대해 단위 테스트 및 통합 테스트 시 반드시 0, NULL, 음수, 매우 큰 수 등의 경계값 케이스를 포함하십시오. CI/CD 파이프라인에 이러한 테스트를 포함시키면 운영 환경 배포 전에 문제를 사전에 발견할 수 있습니다. pgTAP 같은 PostgreSQL 전용 테스트 프레임워크를 활용하면 데이터베이스 레벨 테스트를 체계적으로 관리할 수 있습니다.


관련 에러

  • 22003 (numeric_value_out_of_range): 숫자 값이 허용 범위를 초과할 때 발생하며, NTILE 인자로 매우 큰 정수를 전달할 때 관련될 수 있습니다.
  • 22004 (null_value_not_allowed): NULL 값이 허용되지 않는 컨텍스트에서 NULL이 사용될 때 발생하며, 22014와 함께 윈도우 함수의 인자 검증 오류 계열로 분류됩니다.
  • 42883 (undefined_function): NTILE에 잘못된 타입의 인자를 전달할 경우 함수 자체를 찾지 못하는 에러로 이어질 수 있습니다.
  • 22023 (invalid_parameter_value): 함수 파라미터 자체가 유효하지 않은 경우로, 22014와 유사한 맥락에서 발생하는 윈도우 함수 관련 에러입니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기