MySQL sum() Function
SUM() adds up the values in a column: total revenue, total hours, total stock. Together with GROUP BY, conditions and window functions it answers most "how much?" questions.
1. Basic SUM
Table: bills (6 rows)
| bill_id | outlet | waiter | bill_date | items | amount | tip | payment |
|---|---|---|---|---|---|---|---|
| 1 | Koregaon | Ajay | 2026-03-02 | 3 | 1200.00 | 100.00 | card |
| 2 | Koregaon | Bina | 2026-03-03 | 2 | 800.00 | NULL | cash |
| 3 | Aundh | Chetan | 2026-03-04 | 5 | 1500.00 | 150.00 | upi |
| 4 | Koregaon | Ajay | 2026-03-05 | 1 | 600.00 | 50.00 | cash |
| 5 | Aundh | Chetan | 2026-03-06 | 4 | 900.00 | NULL | upi |
| 6 | Aundh | Deepa | 2026-03-06 | 2 | 450.50 | 20.00 | card |
SELECT SUM(amount) AS revenue, SUM(items) AS items_sold, SUM(tip) AS tips FROM bills;
Result: 1 row
| revenue | items_sold | tips |
|---|---|---|
| 5450.50 | 17 | 320.00 |
Tips include NULLs, which SUM simply skips.
2. SUM with WHERE and GROUP BY
SELECT SUM(amount) AS aundh_revenue FROM bills WHERE outlet = 'Aundh';
Result: 1 row
| aundh_revenue |
|---|
| 2850.50 |
SELECT outlet, SUM(amount) AS revenue, SUM(items) AS items FROM bills GROUP BY outlet ORDER BY revenue DESC;
Result: 2 rows
| outlet | revenue | items |
|---|---|---|
| Aundh | 2850.50 | 11 |
| Koregaon | 2600.00 | 6 |
SELECT waiter, SUM(amount) AS sales FROM bills GROUP BY waiter HAVING SUM(amount) > 1000;
Result: 2 rows
| waiter | sales |
|---|---|
| Ajay | 1800.00 |
| Chetan | 2400.00 |
3. NULLs and Empty Results
SUM of no rows (or only NULLs) is NULL, not 0. Wrap it in COALESCE for reports:
SELECT SUM(amount) AS raw_sum, COALESCE(SUM(amount), 0) AS safe_sum FROM bills WHERE outlet = 'Baner';
Result: 1 row
| raw_sum | safe_sum |
|---|---|
| NULL | 0.00 |
SELECT SUM(tip) AS tips_bina FROM bills WHERE waiter = 'Bina'; -- only NULL tips
Result: 1 row
| tips_bina |
|---|
| NULL |
4. Summing Expressions
SELECT SUM(amount + COALESCE(tip, 0)) AS amount_with_tips, SUM(amount * 0.05) AS gst_5pct FROM bills;
Result: 1 row
| amount_with_tips | gst_5pct |
|---|---|
| 5770.50 | 272.5250 |
SUM(amount + tip) without COALESCE would skip whole bills that have a NULL tip, because amount + NULL is NULL.SELECT SUM(amount + tip) AS wrong, SUM(amount + COALESCE(tip, 0)) AS right_total FROM bills;
Result: 1 row
| wrong | right_total |
|---|---|
| 4070.50 | 5770.50 |
5. Conditional Sums (Pivot)
Sum only the rows that match a condition, several at once. This turns rows into columns (a pivot):
SELECT outlet,
SUM(CASE WHEN payment = 'cash' THEN amount ELSE 0 END) AS cash,
SUM(CASE WHEN payment = 'card' THEN amount ELSE 0 END) AS card,
SUM(CASE WHEN payment = 'upi' THEN amount ELSE 0 END) AS upi,
SUM(amount) AS total
FROM bills
GROUP BY outlet;
Result: 2 rows
| outlet | cash | card | upi | total |
|---|---|---|---|---|
| Koregaon | 1400.00 | 1200.00 | 0.00 | 2600.00 |
| Aundh | 0.00 | 450.50 | 2400.00 | 2850.50 |
A shorter MySQL form: SUM(IF(payment = 'cash', amount, 0)).
6. SUM(DISTINCT)
SUM(DISTINCT col) adds each different value once. It's rarely what you want for money (two bills of ₹900 are both real), so use it with care:
SELECT SUM(items) AS items_total, SUM(DISTINCT items) AS distinct_item_counts FROM bills;
Result: 1 row
| items_total | distinct_item_counts |
|---|---|
| 17 | 15 |
7. Running Totals with SUM() OVER
SELECT bill_date, outlet, amount,
SUM(amount) OVER (ORDER BY bill_date, bill_id) AS running_total,
SUM(amount) OVER (PARTITION BY outlet ORDER BY bill_date, bill_id) AS outlet_running,
ROUND(100 * amount / SUM(amount) OVER (), 1) AS pct_of_all
FROM bills
ORDER BY bill_date, bill_id;
Result: 6 rows
| bill_date | outlet | amount | running_total | outlet_running | pct_of_all |
|---|---|---|---|---|---|
| 2026-03-02 | Koregaon | 1200.00 | 1200.00 | 1200.00 | 22.0 |
| 2026-03-03 | Koregaon | 800.00 | 2000.00 | 2000.00 | 14.7 |
| 2026-03-04 | Aundh | 1500.00 | 3500.00 | 1500.00 | 27.5 |
| 2026-03-05 | Koregaon | 600.00 | 4100.00 | 2600.00 | 11.0 |
| 2026-03-06 | Aundh | 900.00 | 5000.00 | 2400.00 | 16.5 |
| 2026-03-06 | Aundh | 450.50 | 5450.50 | 2850.50 | 8.3 |
8. Result Types and Precision
- SUM of integers or DECIMAL returns DECIMAL, so totals stay exact.
- SUM of FLOAT/DOUBLE returns DOUBLE and may show tiny rounding errors; store money as DECIMAL.
- If a DECIMAL total exceeds 65 digits, MySQL raises an out-of-range error.
SELECT 0.1 + 0.2 AS decimal_math, CAST(0.1 AS DOUBLE) + CAST(0.2 AS DOUBLE) AS double_math;
Result: 1 row
| decimal_math | double_math |
|---|---|
| 0.3 | 0.30000000000000004 |
9. Practice Exercises
Exercise 1: Revenue per day, including a day total of tips (0 if none).
SELECT bill_date, SUM(amount) AS revenue, COALESCE(SUM(tip), 0) AS tips
FROM bills GROUP BY bill_date ORDER BY bill_date;
Result: 5 rows
| bill_date | revenue | tips |
|---|---|---|
| 2026-03-02 | 1200.00 | 100.00 |
| 2026-03-03 | 800.00 | 0.00 |
| 2026-03-04 | 1500.00 | 150.00 |
| 2026-03-05 | 600.00 | 50.00 |
| 2026-03-06 | 1350.50 | 20.00 |
Exercise 2: Each waiter's share of total revenue as a percentage.
SELECT waiter, SUM(amount) AS sales,
ROUND(100 * SUM(amount) / (SELECT SUM(amount) FROM bills), 1) AS pct
FROM bills GROUP BY waiter ORDER BY sales DESC;
Result: 4 rows
| waiter | sales | pct |
|---|---|---|
| Chetan | 2400.00 | 44.0 |
| Ajay | 1800.00 | 33.0 |
| Bina | 800.00 | 14.7 |
| Deepa | 450.50 | 8.3 |
10. Summary
SUM(col)adds up non-NULL values; SUM of nothing is NULL, so use COALESCE.- Combine with WHERE, GROUP BY and HAVING for filtered and grouped totals.
- Watch NULLs inside expressions; COALESCE them first.
SUM(CASE …)builds pivots;SUM() OVER (ORDER BY …)builds running totals.- Use DECIMAL for exact money totals.