COALESCE in PostgreSQL

Learn how to use the PostgreSQL COALESCE function to return the first non-null value from a list of arguments and substitute meaningful defaults for NULL columns.

4 min read · Back to overview

Quick Answer

COALESCE(arg1, arg2, ...) returns the first argument that is not NULL, evaluating left to right. If all arguments are NULL it returns NULL. It is the standard way to replace NULL values with a fallback in SELECT output, calculations, and JOIN results.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

The COALESCE() function accepts any number of arguments and returns the first one that is not NULL. It evaluates arguments left to right and stops as soon as it finds a non-NULL value—remaining arguments are not evaluated. This makes it the idiomatic way to provide fallback values for nullable columns and prevent NULL propagation in calculations.

Syntax

COALESCE(argument_1, argument_2, ...)

Quick scalar examples:

SELECT COALESCE(1, 2);          -- returns 1 (first non-null)
SELECT COALESCE(NULL, 2, 1);    -- returns 2 (skips null, returns next non-null)
SELECT COALESCE(NULL, NULL, 3); -- returns 3
SELECT COALESCE(NULL, NULL);    -- returns NULL (all arguments are null)

Practical Example

A product catalog where some items have a promotional discount. Without COALESCE, arithmetic on a NULL discount produces a NULL net price:

CREATE TABLE products (
  id            SERIAL PRIMARY KEY,
  name          VARCHAR(100) NOT NULL,
  list_price    NUMERIC(10, 2) NOT NULL,
  discount      NUMERIC(10, 2)   -- nullable: NULL means no discount
);

INSERT INTO products (name, list_price, discount) VALUES
  ('Bolt Pack',      12.00, 2.00),
  ('Hex Wrench Set', 45.00, 5.00),
  ('Safety Goggles', 18.00, NULL),
  ('Work Gloves',     8.50, NULL);

-- Without COALESCE: rows with NULL discount show NULL net_price
SELECT
  name,
  list_price,
  discount,
  list_price - discount AS net_price
FROM products;
       name       | list_price | discount | net_price
------------------+------------+----------+-----------
 Bolt Pack        |      12.00 |     2.00 |     10.00
 Hex Wrench Set   |      45.00 |     5.00 |     40.00
 Safety Goggles   |      18.00 |     NULL |      NULL
 Work Gloves      |       8.50 |     NULL |      NULL

Use COALESCE to treat NULL discount as zero:

SELECT
  name,
  list_price,
  COALESCE(discount, 0)            AS effective_discount,
  list_price - COALESCE(discount, 0) AS net_price
FROM products;
       name       | list_price | effective_discount | net_price
------------------+------------+--------------------+-----------
 Bolt Pack        |      12.00 |               2.00 |     10.00
 Hex Wrench Set   |      45.00 |               5.00 |     40.00
 Safety Goggles   |      18.00 |               0.00 |     18.00
 Work Gloves      |       8.50 |               0.00 |      8.50

Multi-Level Fallbacks and Combining with NULLIF

Arguments can be arbitrary expressions, not just constants. Chain multiple fallback sources:

CREATE TABLE events (
  id           SERIAL PRIMARY KEY,
  title        VARCHAR(200) NOT NULL,
  short_desc   TEXT,      -- preferred display text, may be null
  full_desc    TEXT,      -- longer fallback, may be null
  auto_summary TEXT       -- machine-generated fallback
);

SELECT
  id,
  title,
  COALESCE(short_desc, LEFT(full_desc, 160), auto_summary, 'No description available') AS display_text
FROM events;

Combine with NULLIF to treat empty strings the same as NULL:

-- NULLIF(short_desc, '') converts '' to NULL so COALESCE can skip it
SELECT
  id,
  title,
  COALESCE(NULLIF(short_desc, ''), LEFT(full_desc, 160), 'No description') AS display_text
FROM events;

Verify NULL handling in pg_stats after an ANALYZE run:

SELECT attname, null_frac, n_distinct
FROM pg_stats
WHERE tablename = 'products' AND attname = 'discount';

Testing with Vela

Null-handling logic is sensitive to real data distributions. Create a Vela branch from your production snapshot to test COALESCE chains against actual NULL proportions in each column. Running the query on a branch lets you verify that edge cases—all-NULL rows, mixed-type fallbacks—behave correctly before deploying the change.

Production Tips

  • COALESCE is the SQL-standard equivalent of IFNULL (MySQL) and NVL (Oracle). Prefer it for cross-database portability.
  • Avoid wrapping indexed columns in COALESCE inside WHERE clauses. WHERE COALESCE(col, 0) = 0 disables index use; rewrite as WHERE col IS NULL OR col = 0.
  • All arguments must resolve to compatible types; PostgreSQL raises an error if types cannot be unified. Use explicit casts when combining columns of different numeric types.
  • Arguments after the first non-NULL value are never evaluated—this is safe for expensive subqueries but can mask errors in later arguments if not tested separately.
  • For nullable columns that frequently need a default in SELECT output, consider a generated column or a view with COALESCE baked in to keep application queries simpler.

Continue in Conditional Expressions & Operators: ISNULL.

Related in this section: CASE · ISNULL · NULLIF

Frequently Asked Questions

What does COALESCE do in PostgreSQL?
COALESCE scans its argument list from left to right and returns the first value that is not NULL. It is the idiomatic way to substitute a default when a column or expression might be NULL—equivalent to an if-not-null-else chain.
Does COALESCE lock the table or affect performance?
No, COALESCE is a scalar function evaluated at the expression level and has no locking implications. However, using COALESCE in a WHERE clause (e.g., WHERE COALESCE(col, 0) = 0) typically prevents index use on col. Rewrite as col IS NULL OR col = 0 to keep index access.
Can COALESCE accept more than two arguments?
Yes. COALESCE accepts any number of arguments and returns the first non-NULL one. This enables multi-level fallback chains such as COALESCE(preferred_email, work_email, 'noreply@example').
Is COALESCE evaluated lazily?
Yes. PostgreSQL stops evaluating arguments as soon as it finds the first non-NULL value—arguments to the right of that value are never evaluated. This matters when later arguments involve expensive subqueries or functions with side effects.
What is the safest way to handle NULL in arithmetic expressions in production?
Wrap nullable numeric columns with COALESCE(col, 0) before arithmetic. Any arithmetic involving NULL propagates as NULL, so an unguarded expression like price - discount returns NULL whenever discount is NULL. COALESCE ensures a defined numeric result.