Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Conditional aggregation applies a different predicate inside each aggregate, allowing one grouped query to calculate several metrics from the same rows. The portable form is SUM(CASE WHEN ... THEN ... ELSE ... END); databases that support it can use the shorter FILTER (WHERE ...) clause.

SELECT
    customer_id,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_orders,
    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_orders,
    SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue
FROM orders
GROUP BY customer_id;

What conditional aggregation solves

GROUP BY creates one reporting row per group. Each aggregate then evaluates its own condition, so the same input rows can produce several measures without separate queries.

order_id customer_id status amount
1 101 paid 120
2 101 pending 80
3 101 cancelled 40
4 102 paid 200
5 102 paid 50
SELECT
    customer_id,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_orders,
    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_orders,
    SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue
FROM orders
GROUP BY customer_id;
customer_id paid_orders pending_orders paid_revenue
101 1 1 120
102 2 0 250

Running separate queries would require separate scans or result-combining logic. A top-level WHERE status = 'paid' would remove pending and cancelled rows before the other aggregates could see them.

The core patterns

Count matching rows with SUM(CASE)

SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_count

Every matching row contributes 1 and every other row contributes 0. This is explicit and broadly portable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
#1 Best Overall
Elebase USB to USB C Adapter for iPhone 18 Pro Max,USBC Car Charger Adapter
  • Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
  • Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
  • Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
  • Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
  • 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.

Count matching rows with COUNT(CASE)

COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_count

COUNT(expression) counts non-NULL expression results. A matching branch returns 1; with no ELSE, a nonmatching branch returns NULL. Use a guaranteed non-NULL marker such as 1, not a nullable business column.

-- Risky when customer_id can be NULL
COUNT(CASE WHEN status = 'paid' THEN customer_id END)

-- Safer
COUNT(CASE WHEN status = 'paid' THEN 1 END)

Use COUNT(*) for an unconditional row count.

Conditional sums

SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue

If amount can be NULL, a matching row may contribute NULL. Normalize it only when a missing amount should mean zero:

SUM(CASE
        WHEN status = 'paid' THEN COALESCE(amount, 0)
        ELSE 0
    END) AS paid_revenue

Conditional minimum and maximum

MAX(CASE WHEN status = 'paid' THEN amount END) AS largest_paid_order,
MIN(CASE WHEN status = 'paid' THEN order_date END) AS first_paid_order_date

Leaving unmatched rows as NULL prevents an artificial zero, empty string, or date from becoming the result.

Conditional averages

AVG(CASE WHEN status = 'paid' THEN amount END) AS average_paid_order

Do not normally write ELSE 0 here. Zero-valued nonmatching rows would enter the denominator and lower the average. Most built-in aggregates ignore NULL inputs, although exact behavior depends on the function and engine; PostgreSQL documents this qualification at its aggregate-function reference.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

ELSE 0, implicit NULL, and empty results

For count-like sums, use ELSE 0 when you want a group with no matches to produce a numeric zero. An omitted ELSE returns NULL for nonmatches, and SUM can itself return NULL when it receives no non-NULL values.

COALESCE(
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END),
    0
) AS paid_count

For sums, COALESCE expresses a business decision:

  • No matching rows.
  • Matching rows whose measures are all NULL.
  • Matching rows whose true total is zero.

Those states may or may not be equivalent. Apply COALESCE only when they should be reported identically.

Rank #2
Anker USB-C Hub, 5-in-1 USB Hub for Laptops, 4K HDMI Multiport Adapter
  • 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
  • 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
  • Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
  • 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
  • What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.

WHERE, conditional aggregates, and HAVING

WHERE filters every aggregate input

SELECT customer_id, COUNT(*) AS paid_orders
FROM orders
WHERE status = 'paid'
GROUP BY customer_id;

This is appropriate when every metric concerns paid orders, but it cannot also calculate pending orders from the same query block.

Conditions inside aggregates filter one metric

SELECT
    customer_id,
    COUNT(*) AS all_orders,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_orders,
    SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_orders
FROM orders
GROUP BY customer_id;

HAVING filters completed groups

SELECT
    customer_id,
    SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue
FROM orders
GROUP BY customer_id
HAVING SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) > 500;

WHERE restricts rows before grouping; HAVING removes groups after aggregation. PostgreSQL describes this distinction in its table-expression documentation and notes that nonaggregate row restrictions generally belong in WHERE.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Combining all three

SELECT
    customer_id,
    SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue,
    SUM(CASE WHEN status = 'refunded' THEN amount ELSE 0 END) AS refunded_amount
FROM orders
WHERE order_date >= DATE '2026-01-01'
GROUP BY customer_id
HAVING COUNT(*) >= 5;

The date limits the reporting period, each aggregate separates a metric, and HAVING keeps customers with at least five period rows.

The FILTER (WHERE ...) alternative

PostgreSQL and DuckDB support a per-aggregate filter:

SELECT
    customer_id,
    COUNT(*) AS all_orders,
    COUNT(*) FILTER (WHERE status = 'paid') AS paid_orders,
    SUM(amount) FILTER (WHERE status = 'paid') AS paid_revenue
FROM orders
GROUP BY customer_id;

PostgreSQL documents the syntax in aggregate expressions and its aggregate tutorial. DuckDB documents localized filtering, pivot use, and cleaner handling for collection aggregates at its FILTER reference. Do not assume that every database accepts this syntax.

Pattern Strength Limitation
SUM(CASE ...) Familiar and broadly portable More verbose; NULL choices are explicit
COUNT(CASE ...) Direct conditional count Easy to count a nullable expression accidentally
COUNT(*) FILTER (WHERE ...) Concise and semantic Support varies by engine
SUM(...) FILTER (WHERE ...) Clean conditional sum or collection input Check dialect compatibility

Several conditions, thresholds, and buckets

SELECT
    region,
    COUNT(*) AS total_orders,
    SUM(CASE WHEN amount >= 1000 THEN 1 ELSE 0 END) AS large_orders,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_orders,
    SUM(CASE
            WHEN status = 'paid' AND amount >= 1000
            THEN amount
            ELSE 0
        END) AS large_paid_revenue
FROM orders
GROUP BY region;

Each expression is independent. A row can contribute to several metrics when predicates overlap. That is correct for threshold measures such as “at least 100” and “at least 500.”

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #3
Sale
Anker USB C Hub, 7in1 Multi-Port USB Adapter, 4K@60Hz USBC to HDMI Splitter
  • Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
  • Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
  • Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
  • Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
  • What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.

For mutually exclusive buckets, make both boundaries explicit:

SUM(CASE WHEN amount < 100 THEN 1 ELSE 0 END) AS under_100,
SUM(CASE WHEN amount >= 100 AND amount < 500 THEN 1 ELSE 0 END) AS from_100_to_499,
SUM(CASE WHEN amount >= 500 THEN 1 ELSE 0 END) AS 500_or_more

Do not define adjacent groups as <= 100 and >= 100 unless overlap is intentional. For exhaustive buckets, reconcile their sum with the total count.

Rates, percentages, and denominators

SELECT
    region,
    SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) * 1.0
        / NULLIF(COUNT(*), 0) AS paid_rate
FROM orders
GROUP BY region;

The decimal literal (or an explicit cast) avoids integer division where integer operands would truncate. NULLIF prevents division by zero. Multiply by 100 only when the result should be a percentage.

100.0 * COUNT(CASE WHEN status = 'paid' THEN 1 END)
       / NULLIF(COUNT(*), 0) AS paid_percentage

Define the denominator before writing the expression: paid orders divided by all orders is not the same as paid revenue divided by all revenue. A group-level weighted rate is also different from averaging row-level percentages.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Conditional DISTINCT counts

SELECT
    campaign_id,
    COUNT(DISTINCT CASE WHEN converted = 1 THEN user_id END) AS converted_users
FROM events
GROUP BY campaign_id;

Where supported, the equivalent is:

COUNT(DISTINCT user_id) FILTER (WHERE converted = 1) AS converted_users

Event tables often contain multiple rows per user. Count distinct users when the metric is users, rather than counting events. Conversely, SUM(DISTINCT amount) removes duplicate numeric values, not duplicate orders: two different orders worth 100 would be counted once. Pre-aggregate at the required entity grain instead.

Joins, grain, and inflated metrics

Always identify the grain of the result—row, order, customer, session, or another entity. Joining two independent one-to-many tables before aggregation can create a customer × orders × payments row set and multiply every count and sum.

Rank #4
Sale
UGREEN USB to USB C Adapter Combo 4-Pack, 10Gbps USB C Converter Space Gray
  • Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
  • Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
  • Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
  • Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
  • Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
-- Potentially inflated: both child tables are one-to-many
SELECT
    c.customer_id,
    COUNT(CASE WHEN o.status = 'paid' THEN 1 END) AS paid_orders,
    SUM(p.amount) AS payments
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.customer_id
LEFT JOIN payments p ON p.customer_id = c.customer_id
GROUP BY c.customer_id;

Aggregate each relationship first, then join the one-row-per-customer results:

WITH order_metrics AS (
    SELECT
        customer_id,
        COUNT(*) AS total_orders,
        SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_orders
    FROM orders
    GROUP BY customer_id
),
payment_metrics AS (
    SELECT customer_id, SUM(amount) AS total_payments
    FROM payments
    GROUP BY customer_id
)
SELECT
    c.customer_id,
    COALESCE(o.total_orders, 0) AS total_orders,
    COALESCE(o.paid_orders, 0) AS paid_orders,
    COALESCE(p.total_payments, 0) AS total_payments
FROM customers c
LEFT JOIN order_metrics o ON o.customer_id = c.customer_id
LEFT JOIN payment_metrics p ON p.customer_id = c.customer_id;

COUNT(DISTINCT ...) can correct one entity count, but it does not generally repair inflated sums or an incorrect join grain.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

NULL and three-valued logic

Unknown conditions

A comparison with NULL normally evaluates to UNKNOWN, not true. Handle a null status explicitly when it has business meaning:

SUM(CASE WHEN status IS NULL THEN 1 ELSE 0 END) AS missing_status

Nullable measures

In SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END), a matching null amount contributes null. If all matching amounts are null, the aggregate may be null. Replacing it with COALESCE(amount, 0) changes the metric and should be deliberate.

Outer joins

A parent retained by a LEFT JOIN has null child columns when no child exists. Decide whether that parent should receive zero, null, or an explicit “no records” state.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Dates and timestamps

Use half-open intervals: include the start and exclude the next boundary.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Best Value
Anker USB C Hub, 5-in-1 USBC to HDMI Splitter with 4K Display
  • 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
  • Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
  • Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
  • HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
  • What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
SUM(CASE
        WHEN created_at >= TIMESTAMP '2026-01-01 00:00:00'
         AND created_at <  TIMESTAMP '2026-02-01 00:00:00'
        THEN 1
        ELSE 0
    END) AS january_rows

For date columns:

SUM(CASE
        WHEN order_date >= DATE '2026-01-01'
         AND order_date <  DATE '2026-02-01'
        THEN amount
        ELSE 0
    END) AS january_revenue

This avoids losing fractional-second values with an inclusive 23:59:59 endpoint. Date literals, timestamp types, time-zone conversion, session time zones, and date-truncation functions differ by product; account for daylight-saving transitions and whether the column is a date, a timestamp without time zone, or a timestamp with time zone.

Grouping by time and other dimensions

SELECT
    DATE_TRUNC('month', order_date) AS month,
    SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue,
    SUM(CASE WHEN status = 'refunded' THEN amount ELSE 0 END) AS refunded_amount
FROM orders
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY month;

DATE_TRUNC is not portable syntax. Every nonaggregate selected expression generally must be grouped, while aggregate predicates do not replace the GROUP BY. Without GROUP BY, aggregate queries form one overall group; PostgreSQL documents these rules at queries and table expressions.

Grouped aggregates versus window aggregates

Grouped aggregation collapses rows:

SELECT
    department,
    SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) AS active_count
FROM employees
GROUP BY department;

A window aggregate preserves each employee while displaying the department metric:

SELECT
    employee_id,
    department,
    SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END)
        OVER (PARTITION BY department) AS active_count_in_department
FROM employees;

Choose the grouped form for one row per group and the window form when detail rows and group context must coexist. Window support and clause placement still vary by engine.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Conditional aggregation as a manual pivot

SELECT
    region,
    SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid,
    SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS pending,
    SUM(CASE WHEN status = 'cancelled' THEN amount ELSE 0 END) AS cancelled
FROM orders
GROUP BY region;

This is explicit, portable, and suitable when categories are known. It becomes unwieldy when categories are numerous or dynamic; native PIVOT, generated SQL, or a reporting layer may then be more appropriate. DuckDB specifically presents FILTER as useful for pivoted views and host-language generation.

Dialect notes

Feature Portable baseline PostgreSQL DuckDB Snowflake BigQuery
CASE inside an aggregate Primary pattern Supported Supported Supported Supported
FILTER (WHERE ...) Verify per engine Documented Documented Verify current support Verify current support
Date functions and typed literals Dialect-specific Dialect-specific Dialect-specific Dialect-specific Dialect-specific
Integer division Verify behavior Verify behavior Verify behavior Verify behavior Verify behavior
Native pivot Vendor-specific Vendor-specific Vendor-specific Vendor-specific Vendor-specific

Snowflake documents conditional expressions such as CASE, IFF, IFNULL, NULLIF, and COALESCE at its conditional-expression reference. BigQuery documents aggregate-call modifiers at its aggregate-function reference; check the specific function before transferring syntax. Vendor helper functions are not automatically portable.

Failure-proofing checklist

  • What is the input row grain and the desired output grain?
  • Does each condition overlap another condition intentionally?
  • Should nonmatches contribute zero or remain null?
  • Is the expression counted guaranteed to be non-null?
  • Could joins multiply rows?
  • Is the denominator the correct population for the rate?
  • Could integer division truncate the result?
  • Are timestamp boundaries half-open and time-zone aware?
  • Does the target engine support FILTER, date syntax, and the chosen pivot or window syntax?
  • Have results been reconciled with a smaller independently filtered query?

For exhaustive mutually exclusive buckets, verify that the bucket total equals COUNT(*). For risky expressions, do not assume a surrounding CASE controls aggregate evaluation order; PostgreSQL documents that aggregate expressions can be evaluated before other select-list or HAVING expressions at its expression reference. Use a separate query level or a safe expression instead.

Quick reference

-- Conditional count
SUM(CASE WHEN condition THEN 1 ELSE 0 END)

-- Conditional count with COUNT
COUNT(CASE WHEN condition THEN 1 END)

-- Conditional sum
SUM(CASE WHEN condition THEN amount ELSE 0 END)

-- Conditional average
AVG(CASE WHEN condition THEN amount END)

-- Conditional distinct entities
COUNT(DISTINCT CASE WHEN condition THEN entity_id END)

-- Conditional rate
SUM(CASE WHEN condition THEN 1 ELSE 0 END) * 1.0
    / NULLIF(COUNT(*), 0)

-- PostgreSQL/DuckDB alternative
COUNT(*) FILTER (WHERE condition)
SUM(amount) FILTER (WHERE condition)

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.