PostgreSQL error 22012 occurs when an SQL query attempts to divide a numeric value by zero. This runtime error stops query execution and rolls back the transaction. Use NULLIF(), CASE statements, or data filtering to prevent this error.
The PostgreSQL error code 22012 "division_by_zero" occurs when any numeric expression attempts to divide by zero, either a literal zero value or a column that evaluates to zero. This is a strict mathematical rule enforced by the database engine to prevent undefined calculations. When PostgreSQL encounters division by zero, it immediately stops query execution and raises an error with SQLSTATE 22012. This error cannot be suppressed with database settings—you must handle the condition in your SQL logic or application code. The error typically occurs during calculations, financial reports, rate computations, or analytics queries where denominator values aren't pre-validated. Unlike some databases that return NULL or Infinity for division by zero, PostgreSQL treats this as an error condition. NULL values divided by any number correctly return NULL, but dividing by NULL also returns NULL rather than an error—only literal or computed zeros trigger the 22012 error.
Find the query causing the division by zero error by examining your application logs or PostgreSQL error messages. Look for numeric division operations (/ operator) in your SELECT, WHERE, or calculated columns.
-- Example queries that cause division by zero
SELECT user_id, total_sales / total_orders FROM user_stats;
-- ERROR: division by zero (when total_orders is 0)
SELECT profit / expenses FROM financial_reports;
-- ERROR: division by zero (when expenses is 0)
SELECT (a - b) / (c - d) FROM calculations;
-- ERROR: division by zero (when c equals d)Use this template to find your problematic expression: SELECT ... / ... FROM ...
Query your data to identify which rows have zero or null values in the denominator column. This shows the scope of the problem.
-- Find rows where divisor is zero
SELECT id, total_orders, total_sales
FROM user_stats
WHERE total_orders = 0;
-- Count how many rows are affected
SELECT COUNT(*) as affected_rows
FROM financial_reports
WHERE expenses = 0 OR expenses IS NULL;
-- Check for derived zero values
SELECT id, (col_a - col_b) as divisor
FROM my_table
WHERE (col_a - col_b) = 0;Understanding the extent of the issue helps you choose the best fix approach.
The simplest fix is to use NULLIF() to convert zero divisors to NULL. This prevents the error because dividing by NULL returns NULL (not an error). COALESCE() can provide a default value if needed.
-- Basic NULLIF approach
SELECT
user_id,
total_sales / NULLIF(total_orders, 0) as avg_sale
FROM user_stats;
-- With COALESCE to provide a default
SELECT
user_id,
COALESCE(total_sales / NULLIF(total_orders, 0), 0) as avg_sale
FROM user_stats;
-- For multiple divisions
SELECT
id,
revenue / NULLIF(expenses, 0) as profit_ratio,
profit / NULLIF(investment, 0) as roi
FROM financial_data;This is the most concise solution and works well when NULL results are acceptable for zero divisors.
If you want to exclude rows with zero divisors entirely, add a WHERE clause to filter them before calculation. This approach ensures all results are valid numbers.
-- Filter out zero divisors
SELECT
user_id,
total_sales / total_orders as avg_sale
FROM user_stats
WHERE total_orders > 0;
-- Multiple conditions
SELECT
id,
revenue / expenses as profit_margin
FROM financial_reports
WHERE expenses > 0
AND revenue > 0
AND report_year = 2024;
-- With aggregate functions
SELECT
category,
SUM(revenue) / SUM(expenses) as total_margin
FROM sales_data
WHERE expenses > 0
GROUP BY category;This approach is clean and improves performance by reducing the result set size.
For more complex scenarios, use CASE to implement custom logic for handling zero divisors. This gives you full control over edge cases.
-- Return NULL for zero divisors
SELECT
user_id,
CASE
WHEN total_orders > 0 THEN total_sales / total_orders
ELSE NULL
END as avg_sale
FROM user_stats;
-- Return a default value instead of NULL
SELECT
id,
CASE
WHEN expenses > 0 THEN revenue / expenses
ELSE 0 -- or any default value
END as profit_margin
FROM financial_reports;
-- Multi-level logic
SELECT
id,
category,
CASE
WHEN divisor > 0 THEN numerator / divisor
WHEN divisor = 0 THEN NULL
ELSE -1 -- Flag error conditions
END as result
FROM calculations;CASE statements let you handle different conditions distinctly (zero, positive, negative).
The GREATEST() function returns the larger of two values. Use it to ensure the divisor never goes below a safe minimum (usually 1).
-- Ensure divisor is at least 1
SELECT
user_id,
total_sales / GREATEST(total_orders, 1) as avg_sale
FROM user_stats;
-- With multiple columns
SELECT
product_id,
total_revenue / GREATEST(total_units, 1) as avg_price
FROM inventory;
-- In aggregate queries
SELECT
category,
SUM(amount) / GREATEST(COUNT(*), 1) as avg_amount
FROM transactions
GROUP BY category;This ensures the divisor never reaches zero while preserving all rows in your result set.
For long-term solutions, add constraints or validation logic to prevent zero values from entering the database in the first place.
-- Add CHECK constraint to ensure positive values
ALTER TABLE user_stats
ADD CONSTRAINT chk_positive_orders
CHECK (total_orders >= 0);
-- Use DEFAULT values
ALTER TABLE financial_reports
ALTER COLUMN expenses SET DEFAULT 1;
-- Add NOT NULL constraints where zero shouldn't exist
ALTER TABLE calculations
ALTER COLUMN divisor SET NOT NULL;
-- Create index for filtered queries
CREATE INDEX idx_user_stats_active
ON user_stats (user_id)
WHERE total_orders > 0;Prevention through data integrity constraints reduces the need for division by zero handling in queries.
PostgreSQL Null Behavior in Division:
PostgreSQL handles NULL values differently from zero in division:
- value / NULL = NULL (no error)
- NULL / value = NULL (no error)
- value / 0 = ERROR 22012 (division by zero)
This is why NULLIF() is such an effective fix—converting zero to NULL leverages PostgreSQL's NULL handling.
Mathematical Functions and Division by Zero:
Some PostgreSQL functions handle division by zero in specific ways:
- MOD(x, 0) also raises error 22012
- % operator (modulo) raises error 22012
- Standard division (/) raises error 22012
- Integer division (/) raises error 22012
Performance Considerations:
When filtering large datasets, consider these optimizations:
1. Create indexes on commonly filtered columns (WHERE expense > 0)
2. Use materialized views for frequently calculated ratios
3. Pre-calculate ratios during ETL if the data is large
-- Create materialized view to pre-calculate safe ratios
CREATE MATERIALIZED VIEW user_statistics AS
SELECT
user_id,
total_sales / GREATEST(total_orders, 1) as avg_sale,
COUNT(*) as transaction_count
FROM user_stats
GROUP BY user_id;Aggregate Functions and Division by Zero:
When using GROUP BY with division, ensure your group-level divisor is protected:
-- Dangerous: SUM() could be 0
SELECT category, SUM(amount) / SUM(quantity) FROM sales GROUP BY category;
-- Safe: NULLIF protects aggregates
SELECT
category,
SUM(amount) / NULLIF(SUM(quantity), 0)
FROM sales
GROUP BY category;Handling Derived Zero Divisors:
When the divisor is a calculated expression, the same rules apply:
-- Derived zero: (a - a) = 0
SELECT value / NULLIF(a - a, 0) FROM table;
-- Practical example: percentage change where old = new
SELECT
(new_value - old_value) / NULLIF(old_value, 0) as pct_change
FROM metrics;ERROR: syntax error at end of input
Syntax error at end of input in PostgreSQL
Bind message supplies N parameters but prepared statement requires M
Bind message supplies N parameters but prepared statement requires M in PostgreSQL
Multidimensional arrays must have sub-arrays with matching dimensions
Multidimensional arrays must have sub-arrays with matching dimensions
ERROR: value too long for type character varying
Value too long for type character varying
insufficient columns in unique constraint for partition key
How to fix "insufficient columns in unique constraint for partition key" in PostgreSQL