2026년 06월 06일 | DBMS Error 가이드
이 글에서 다루는 내용
22016 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
22016 invalid argument for nth_value function 는?
PostgreSQL 에러 코드 22016는 윈도우 함수 NTH_VALUE()에 잘못된 인자가 전달될 때 발생하는 오류입니다. NTH_VALUE() 함수는 윈도우 프레임 내에서 N번째 행의 값을 반환하는 함수인데, 이 N 값으로 0이나 음수, 또는 NULL이 전달될 경우 해당 에러가 발생합니다. 실무에서는 동적으로 N 값을 계산하거나 사용자 입력을 받아 처리할 때 예상치 못한 값이 들어오면서 이 에러를 마주치는 경우가 많습니다.
주요 발생 원인
- N 값으로 0 또는 음수 전달
NTH_VALUE() 함수의 두 번째 인자(N)는 반드시 1 이상의 양의 정수여야 합니다. 실수로 0 또는 음수를 전달하면 PostgreSQL은 즉시 22016 에러를 발생시킵니다. 특히 동적 쿼리나 변수를 통해 N 값을 계산할 때 경계값 처리를 빠뜨리는 경우가 자주 발생합니다.
- NULL 값이 N 인자로 전달되는 경우
서브쿼리나 계산식의 결과가 NULL이 되어 NTH_VALUE() 함수의 N 인자로 넘어오는 경우에도 이 에러가 발생합니다. 예를 들어, 집계 함수나 CASE 표현식의 결과로 NULL이 반환되고, 이를 N 값으로 사용하면 PostgreSQL이 유효하지 않은 인자로 판단합니다. NULL 처리에 대한 방어 코드가 없으면 예상치 못한 상황에서 에러가 발생합니다.
- 정수가 아닌 값(소수, 문자열 등) 전달
NTH_VALUE()의 N 인자는 정수 타입이어야 하지만, 동적 SQL 또는 타입 캐스팅 과정에서 소수점이 포함된 숫자나 문자열이 전달될 수 있습니다. PostgreSQL은 내부적으로 타입을 엄격하게 검증하기 때문에, 명시적인 형변환 없이 부적합한 타입의 값이 들어오면 에러로 이어집니다. 특히 외부 API나 사용자 입력에서 받아온 값을 별도 검증 없이 바로 사용할 때 이 문제가 자주 나타납니다.
해결 방법
원인 1 해결: N 값 유효성 검사 및 GREATEST 함수 활용
0이나 음수가 들어오지 않도록 GREATEST() 함수를 사용하여 최솟값을 1로 보정하거나, CASE 표현식으로 방어 로직을 추가합니다.
-- 문제가 발생하는 쿼리 (n=0 전달)
SELECT
employee_id,
salary,
NTH_VALUE(salary, 0) OVER (
PARTITION BY department_id
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS nth_salary
FROM employees;
-- ERROR: 22016: argument of nth_value must be a positive integer
-- 해결책 1: GREATEST()로 최솟값 보장
SELECT
employee_id,
salary,
NTH_VALUE(salary, GREATEST(0, 1)) OVER (
PARTITION BY department_id
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS nth_salary
FROM employees;
-- 해결책 2: CASE 표현식으로 방어 처리
DO $$
DECLARE
v_n INTEGER := 0; -- 외부에서 받아온 N 값
BEGIN
IF v_n < 1 THEN
RAISE NOTICE 'N 값이 유효하지 않습니다. 기본값 1로 설정합니다.';
v_n := 1;
END IF;
-- 이후 동적 쿼리에서 v_n 사용
END;
$$;
원인 2 해결: NULL 처리 — COALESCE 활용
N 인자로 들어오는 값이 NULL이 될 가능성이 있다면 COALESCE()로 기본값을 지정합니다.
-- NULL이 전달되어 에러 발생하는 예시
SELECT
product_id,
price,
NTH_VALUE(price, NULL::integer) OVER (
ORDER BY price DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS nth_price
FROM products;
-- ERROR: 22016: argument of nth_value must be a positive integer
-- 해결책: COALESCE로 NULL 방어
WITH ranked AS (
SELECT
product_id,
price,
(SELECT COUNT(*) / 2 FROM products) AS dynamic_n -- NULL 가능성 있는 계산
FROM products
)
SELECT
product_id,
price,
NTH_VALUE(price, COALESCE(dynamic_n::integer, 1)) OVER (
ORDER BY price DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS nth_price
FROM ranked;
원인 3 해결: 명시적 타입 캐스팅
외부 입력값이나 계산 결과를 N 인자로 사용할 때는 반드시 명시적으로 INTEGER로 변환하고, 유효성 검사를 병행합니다.
-- 소수점 값이 들어오는 경우 방어 처리
SELECT
order_id,
amount,
NTH_VALUE(amount, FLOOR(2.7)::integer) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS nth_amount
FROM orders;
-- 사용자 입력값을 안전하게 처리하는 함수 예시
CREATE OR REPLACE FUNCTION safe_nth_value_n(input_n ANYELEMENT)
RETURNS INTEGER AS $$
DECLARE
result INTEGER;
BEGIN
result := FLOOR(input_n::NUMERIC)::INTEGER;
IF result < 1 THEN
RAISE EXCEPTION 'N 값은 1 이상이어야 합니다. 입력값: %', input_n;
END IF;
RETURN result;
END;
$$ LANGUAGE plpgsql;
-- 함수를 활용한 안전한 쿼리
SELECT
order_id,
amount,
NTH_VALUE(amount, safe_nth_value_n(3)) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS nth_amount
FROM orders;
예방 방법
- 입력값 검증 레이어 구축
NTH_VALUE() 함수에 동적 N 값을 전달하는 경우, 애플리케이션 레이어 또는 PL/pgSQL 함수 내에서 반드시 사전 검증 로직을 구현하세요. COALESCE(), GREATEST(), NULLIF() 조합으로 방어 코드를 만들어 두면 운영 중 예기치 않은 값이 들어와도 에러 없이 안전하게 처리할 수 있습니다. 아래와 같은 패턴을 표준으로 사용하면 효과적입니다.
“`sql
— 권장 방어 패턴
SELECT NTH_VALUE(col, GREATEST(COALESCE(dynamic_n, 1), 1))
OVER (ORDER BY col ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM table_name;
“`
- 코드 리뷰 및 테스트 케이스 작성
윈도우 함수를 사용하는 쿼리에 대해 N=0, N=-1, N=NULL 등의 경계값 테스트 케이스를 반드시 포함하세요. CI/CD 파이프라인에 pgTAP 등의 DB 테스트 프레임워크를 연동하여 경계값 시나리오가 자동으로 검증되도록 구성하면, 배포 전에 이 류의 에러를 사전에 차단할 수 있습니다.
관련 에러
- 22003 (numeric_value_out_of_range): N 값이 정수 범위를 초과할 때 발생하며
22016과 함께 윈도우 함수 인자 관련 에러로 자주 언급됩니다. - 42883 (undefined_function): 윈도우 함수 자체의 시그니처가 맞지 않을 때 발생하며, 인자 타입 불일치 문제와 연관됩니다.
- 22012 (division_by_zero): 동적으로 N 값을 나눗셈으로 계산할 때 0으로 나누어 N이 비정상 값이 되는 경우 연쇄적으로 발생할 수 있습니다.
- 2201B (invalid_regular_expression): 같은
22xxx계열의 데이터 예외 클래스로, 함수 인자 유효성 관련 에러 그룹에 속합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.