惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
U
Unit 42
N
Netflix TechBlog - Medium
人人都是产品经理
人人都是产品经理
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
小众软件
小众软件
V
Visual Studio Blog
T
Tailwind CSS Blog
Engineering at Meta
Engineering at Meta
博客园 - 叶小钗
GbyAI
GbyAI
爱范儿
爱范儿
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
D
DataBreaches.Net
博客园_首页
D
Docker
A
About on SuperTechFans
G
Google Developers Blog
I
InfoQ
T
The Blog of Author Tim Ferriss
V
V2EX
博客园 - Franky

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
PostgreSQL 22014 Error: Causes and Solutions Complete Guide
umzzil nng · 2026-06-06 · via DEV Community

umzzil nng

PostgreSQL Error 22014: Invalid Argument for NTILE Function

PostgreSQL error code 22014 is raised when the NTILE(n) window function receives an invalid argument — specifically when n is NULL, 0, or a negative integer. The NTILE(n) function divides a result set into n ranked buckets, so any value that doesn't represent a positive integer makes the operation logically impossible. This error is most commonly encountered in dynamic queries, parameterized functions, or when user-supplied values are passed directly into the function without validation.


Top 3 Causes

1. Passing NULL as the NTILE Argument

The most frequent cause is a NULL value reaching the NTILE() function. This often happens when a subquery or application parameter unexpectedly returns no value.

-- Triggers ERROR 22014
SELECT
    employee_id,
    salary,
    NTILE(NULL) OVER (ORDER BY salary DESC) AS bucket
FROM employees;
-- ERROR:  argument of ntile must be greater than zero

-- A realistic scenario where NULL sneaks in
WITH config AS (
    SELECT NULL::INTEGER AS n  -- value missing from config table
)
SELECT
    employee_id,
    NTILE((SELECT n FROM config)) OVER (ORDER BY salary DESC) AS bucket
FROM employees;

2. Passing Zero or a Negative Integer

Passing 0 or any negative number is equally invalid. This often stems from miscalculated business logic or a misconfigured report parameter.

-- Triggers ERROR 22014 with zero
SELECT
    product_id,
    revenue,
    NTILE(0) OVER (ORDER BY revenue DESC) AS tier
FROM sales;
-- ERROR:  argument of ntile must be greater than zero

-- Triggers ERROR 22014 with a negative value
SELECT
    product_id,
    revenue,
    NTILE(-3) OVER (ORDER BY revenue DESC) AS tier
FROM sales;
-- ERROR:  argument of ntile must be greater than zero

3. Unvalidated Parameters in PL/pgSQL Functions or Dynamic SQL

When wrapping NTILE() inside a PL/pgSQL function or a dynamically built query, failing to validate the input before execution is a common pitfall in production environments.

-- Dangerous: no input validation
CREATE OR REPLACE FUNCTION rank_employees(p_buckets INTEGER)
RETURNS TABLE(emp_id INT, salary NUMERIC, bucket INT) AS $$
BEGIN
    RETURN QUERY
    SELECT
        e.employee_id,
        e.salary,
        NTILE(p_buckets) OVER (ORDER BY e.salary DESC)::INT
    FROM employees e;
    -- Explodes if p_buckets is NULL, 0, or negative!
END;
$$ LANGUAGE plpgsql;

SELECT * FROM rank_employees(0);   -- ERROR 22014
SELECT * FROM rank_employees(NULL); -- ERROR 22014


Quick Fix Solutions

Fix 1: Use COALESCE + GREATEST for Inline Defense

-- Safe pattern: guarantees n >= 1 at all times
SELECT
    employee_id,
    salary,
    NTILE(GREATEST(COALESCE($1, 4), 1)) OVER (ORDER BY salary DESC) AS bucket
FROM employees;

Fix 2: Add Input Validation Inside PL/pgSQL

CREATE OR REPLACE FUNCTION rank_employees(p_buckets INTEGER)
RETURNS TABLE(emp_id INT, salary NUMERIC, bucket INT) AS $$
BEGIN
    IF p_buckets IS NULL OR p_buckets <= 0 THEN
        RAISE EXCEPTION 'p_buckets must be a positive integer, received: %', p_buckets
            USING ERRCODE = '22014';
    END IF;

    RETURN QUERY
    SELECT
        e.employee_id,
        e.salary,
        NTILE(p_buckets) OVER (ORDER BY e.salary DESC)::INT
    FROM employees e;
END;
$$ LANGUAGE plpgsql;

-- Works correctly
SELECT * FROM rank_employees(5);

-- Raises a clear, descriptive error
SELECT * FROM rank_employees(0);

Fix 3: Create a Safe NTILE Wrapper Function

-- Reusable utility to sanitize NTILE input across your codebase
CREATE OR REPLACE FUNCTION safe_ntile_arg(p_n INTEGER, p_default INTEGER DEFAULT 4)
RETURNS INTEGER AS $$
    SELECT GREATEST(COALESCE(p_n, p_default), 1);
$$ LANGUAGE sql IMMUTABLE;

-- Usage
SELECT
    employee_id,
    salary,
    NTILE(safe_ntile_arg($1)) OVER (ORDER BY salary DESC) AS bucket
FROM employees;


Prevention Tips

1. Enforce constraints at the data layer.
If bucket counts are stored in a configuration table, add a CHECK constraint to prevent invalid values from ever being saved.

CREATE TABLE report_settings (
    id           SERIAL PRIMARY KEY,
    report_name  TEXT NOT NULL,
    bucket_count INTEGER NOT NULL CHECK (bucket_count >= 1)
);

2. Always test boundary values.
Include NULL, 0, -1, and 1 in your test suite for any function that uses NTILE(). Catching these at the CI/CD stage is far cheaper than debugging a production incident.

-- Quick boundary check
SELECT NTILE(GREATEST(COALESCE(val, 1), 1)) OVER (ORDER BY id)
FROM generate_series(1, 10) AS t(id)
CROSS JOIN (VALUES (NULL), (0), (-1), (1), (5)) AS v(val);


Related Errors

Error Code Name Relation
22012 division_by_zero Similar arithmetic window function error
22003 numeric_value_out_of_range NTILE argument exceeds INTEGER bounds
42883 undefined_function Wrong argument type passed to NTILE

📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.