SQL SUM Function: Totals, NULL and Duplicate Rows

SUM() adds the non-NULL numeric values in an input group, returning NULL when there are no known values to add.

Lesson 39 of 102Intermediate10 min read

SUM() adds the non-NULL numeric values in an input group, returning NULL when there are no known values to add.

Total revenue is usually an addition question, but the important details are which records are included and what happens when amounts are missing. SUM will calculate exactly the values supplied by the query, including any unintended duplicates created by joins.

This reference adds detail to the main course. You can read it independently: the small database below supplies every table and row used in the examples. Start with the question each query answers, then compare the returned values with the data rather than memorising a line of syntax.

Set up the practice database

Run this setup once in a new practice database, before the examples. It uses four customers and six sales. Ana has two sales, Ben has one, Cara has two, and Dev has none.

Sale 106 has no customer link; sale 104 has an unknown amount. These deliberate test cases expose missing information and unmatched relationships.

SQL
CREATE TABLE customers (
  id INTEGER PRIMARY KEY,
  name VARCHAR(40) NOT NULL,
  city VARCHAR(40),
  manager_id INTEGER
);
CREATE TABLE sales (
  id INTEGER PRIMARY KEY,
  customer_id INTEGER,
  amount DECIMAL(10,2),
  status VARCHAR(20) NOT NULL,
  created_on DATE NOT NULL
);
INSERT INTO customers (id,name,city,manager_id) VALUES
  (1,'Ana','Leeds',NULL),(2,'Ben','Leeds',1),
  (3,'Cara','Porto',1),(4,'Dev',NULL,2);
INSERT INTO sales (id,customer_id,amount,status,created_on) VALUES
  (101,1,10,'paid','2026-01-01'),
  (102,1,30,'pending','2026-01-02'),
  (103,2,20,'paid','2026-01-02'),
  (104,3,NULL,'pending','2026-02-01'),
  (105,3,20,'paid','2026-02-02'),
  (106,NULL,5,'paid','2026-02-03');

An amount of zero would be a known value. The NULL amount here means the value is unknown. A customer with no matching sale differs from a sale whose customer_id is NULL.

The primary keys identify individual records even when amounts or cities repeat. Dates use year-month-day format. The examples specify sorting whenever order matters; insertion order alone does not guarantee display order.

How SUM() works

SUM(amount) adds ten, thirty, twenty, twenty and five, returning eighty-five. Sale 104 has no known amount and therefore contributes no numeric value. This is not the same as proving that sale has a zero amount. Pair SUM with a count of known and unknown amounts if completeness matters, so a reader can distinguish a full total from a total of currently known values.

SUM returns NULL when no non-NULL input is available, rather than always returning zero. COALESCE(SUM(amount),0) can provide a display or reporting default if an empty selection should mean no revenue. Decide carefully whether the same replacement is appropriate for an all-unknown selection. Zero says a known numeric total; NULL says no total could be derived from known input. Moving a fallback inside or outside the aggregate can change the interpretation of missing values.

Totals per group and conditional totals

GROUP BY status produces a separate total for paid and pending sales. Paid amounts total fifty-five and known pending amounts total thirty. Conditional aggregation can compute those measures as separate columns in one summary by applying CASE inside SUM. Adding quantity times unit_price is another useful pattern when the source represents line items, but rounding and numeric precision should be defined before the expression is used for financial reporting.

SUM(DISTINCT amount) adds different amount values only, not distinct sale records. Here it returns sixty-five because the repeated twenty contributes once. That is not a valid total of sales: two real purchases can have the same price. When a join accidentally repeats transactions, fix the join or aggregate each relation at its intended grain. DISTINCT on the amount can undercount legitimate repeated values while appearing to correct duplicated rows. Use a decimal type with suitable precision for money; floating-point storage can introduce rounding surprises.

MySQL, PostgreSQL, SQL Server and SQLite

SUM is supported by all four systems, but numeric result types and overflow rules differ. PostgreSQL and MySQL preserve exact numeric arithmetic for suitable decimal inputs; SQLite’s ordinary DECIMAL declaration gives numeric affinity rather than fixed decimal storage. For strict currency calculations in SQLite, consider integer minor units with an explicit unit policy.

Check the engine-specific syntax and version requirements in SQLite aggregate SUM.

Practice task and worked solution

Calculate the total known amount for paid sales only. Keep the filter outside the aggregate because the entire input is restricted to paid records.

Try the task before reading its worked solution. The supplied rows make the expected answer checkable by hand.

SQL
SELECT SUM(amount) AS paid_total
FROM sales WHERE status='paid';
Output
paid_total
55

The qualifying amounts are ten, twenty, twenty and five. Adding them gives fifty-five. The pending thirty and missing amount do not enter this input. The query includes the anonymous paid sale because the task concerns all paid sales rather than identified customers only.

Compare SUM(amount) with SUM(DISTINCT amount): eighty-five versus sixty-five exposes the legitimate repeated twenty. Filter to only sale 104 and observe NULL, then apply COALESCE to the final sum and explain the policy behind the resulting zero. Filter to pending status and pair SUM with COUNT(*) and COUNT(amount) to show why thirty is only a known-value subtotal.

SQL SUM Function: Totals, NULL and Duplicate Rows Syntax

SUM()

SELECT SUM(amount) AS known_total FROM sales;

SQL SUM Function: Totals, NULL and Duplicate Rows Summary

  • SUM(column) adds the non-NULL values; over no known values it returns NULL, not 0.
  • GROUP BY produces one total per group, and SUM(CASE WHEN ... THEN amount END) produces conditional totals in one row.
  • Report the number of missing values beside a total, so an incomplete sum isn't mistaken for a complete one.
  • SUM(DISTINCT amount) drops genuinely different transactions that share a value; fix duplicated join rows instead.
  • Use exact DECIMAL types or whole minor units, such as cents, for money totals.

SQL SUM Function: Totals, NULL and Duplicate Rows Examples

Each example shows the code, the output it produces and what each part does.

Example 1

The total of known sale amounts

The addition is 10 + 30 + 20 + 20 + 5. The missing value is ignored rather than counted as a known zero. The output is a known-value total and does not establish that all six sale amounts are complete.

SQL
SELECT SUM(amount) AS known_total FROM sales;
Output
known_total
85

Common SQL SUM Function: Totals, NULL and Duplicate Rows Mistakes

Using SUM(DISTINCT) to fix duplicate rows

SUM(DISTINCT amount) is not a general cure for duplicate joined sales. It removes equal values even when they belong to genuinely different transactions. Sales 103 and 105 are both twenty and both belong in the total. Preserve transaction identity while fixing row multiplication.

SQL SUM Function: Totals, NULL and Duplicate Rows in Real Projects

Paid and pending totals in a payment report

A payment report can show paid revenue separately from pending known value. Include a missing-amount count so the pending total is not mistaken for a complete receivable balance. Define the currency and rounding method outside a vague label such as total, especially when several currencies can appear.

For monetary totals, define the source grain and currency before adding values. Two purchases with equal amounts must both contribute. Include missing-amount information alongside a known-value subtotal so the consumer understands whether reconciliation is complete. A fallback zero should communicate a genuine no-value policy rather than conceal incomplete input.

SQL SUM Function: Totals, NULL and Duplicate Rows FAQ

Does SUM include NULL as zero?

No. It skips NULL, and the result over no known values is NULL, so the distinction remains observable. Wrap the result in COALESCE(SUM(amount), 0) when a report should show 0 for "no sales", which is usually the right display choice.

Can SUM add text?

Some databases quietly convert text that looks like numbers, and others raise an error. Conversion rules vary and can conceal bad data, such as '12,50' becoming 12. Store numeric measures using an appropriate numeric type and convert imported text explicitly with CAST after validating it.

Should money always use floating point?

No. Binary floating point can only approximate decimal fractions, so totals can drift by fractions of a cent. Choose an exact DECIMAL(12, 2) column or an integer number of minor units, such as cents, suitable for the engine.

Why is my total too high after a join?

Joining a table to a "many" side repeats each row once per match, so its amount is added several times. Sum each table at its own level first, for example sales per customer in a subquery, and then join the totals. Checking COUNT(*) before and after the join reveals the multiplication.

How do I show a running total instead of one total?

Use SUM as a window function: SUM(amount) OVER (ORDER BY created_on, id) adds each sale to the total of all earlier ones while keeping every row. The running totals lesson covers frames and moving totals.

SQL SUM Function: Totals, NULL and Duplicate Rows Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choicemedium

What is the known amount total for paid sales?

SQL SUM Function: Totals, NULL and Duplicate Rows Quiz

2 questions to check what you learned.

  1. What is SUM(amount) over this dataset?

    • 85
    • 65, because equal amounts contribute once
    • NULL, because one sale amount is unknown
  2. Why is SUM(DISTINCT amount) not the total revenue here?

    • It removes the anonymous sale rather than duplicate amounts
    • It combines equal amounts from different real sales
    • It excludes every amount on a pending sale automatically