Calculations and rounding

Arithmetic row by row, and the four traps behind most wrong numbers in reports.

Beginner8 min readLesson 10 of 38
In this lesson 6 sections
  1. Trap 1: order of operations
  2. Trap 2: integer division
  3. Trap 3: dividing by zero
  4. Trap 4: NULL in math
  5. Rounding
  6. Snowflake notes
Jump to practice

SQL does math on each row, like a spreadsheet formula copied down a column.

SELECT opportunity_id,
       amount + 5000          AS with_onboarding_fee,
       amount - 2500          AS after_discount,
       amount * 1.03          AS after_3pct_increase,
       amount / 12            AS per_month
FROM opportunities;

Trap 1: order of operations

* and / run before + and -, so 100 + 50 * 2 is 200, not 300. Use parentheses whenever the order matters:

SELECT (amount + 5000) * 1.03 AS fee_then_increase
FROM opportunities;

Trap 2: integer division

Dividing one whole number by another behaves differently by database:

SELECT 7 / 2;   -- MySQL: 3.5     SQL Server, PostgreSQL, SQLite: 3

A win rate of 45 out of 50 comes back as 0 in SQL Server. Make one side a decimal first, and it works everywhere:

SELECT 7 * 1.0 / 2;   -- 3.5 on every database

Trap 3: dividing by zero

On SQL Server and PostgreSQL, dividing by zero is an error that stops the whole query. (MySQL and SQLite quietly return NULL instead.) NULLIF(x, 0) turns a zero into NULL, and dividing by NULL just returns NULL:

SELECT 45 / 0;                -- error on SQL Server and PostgreSQL
SELECT 45 / NULLIF(0, 0);     -- NULL, and the query keeps running
 
-- The real-world shape: a win rate for a rep who hasn't closed anything yet
won_deals * 1.0 / NULLIF(closed_deals, 0)

You’ll build counts like won_deals in the next lesson.

Guard every division whose bottom number could ever be zero.

Trap 4: NULL in math

Any arithmetic with NULL returns NULL:

SELECT 100 + NULL;                  -- NULL
SELECT 100 + COALESCE(NULL, 0);     -- 100: treat missing as zero on purpose

Decide whether a missing value means “unknown” or “zero”, then write the query to match.

Rounding

Function Result What it does
ROUND(1234.567, 2) 1234.57 Nearest, to 2 decimals
ROUND(1234.567, 0) 1235 Nearest whole number
ROUND(1234.567, -2) 1200 Negative means left of the decimal point
CEIL(4.1) 5 Always up
FLOOR(4.9) 4 Always down

SQL Server calls it CEILING instead of CEIL. SQLite, which the sandbox below runs on, ignores negative digits, so ROUND(1234.567, -2) gives 1235 there.

Round only in the final result. Rounding each row and then adding them up builds in error.

Snowflake notes

Snowflake returns 3.5 for 7 / 2: dividing two whole numbers gives a decimal, like MySQL. Dividing by zero is an error, like SQL Server. Snowflake has two shortcuts for the guard:

Function Returns when the bottom number is 0
DIV0(a, b) 0
DIV0NULL(a, b) 0, and also when b is NULL

NULLIF returns NULL instead, which works on every database. Pick on purpose: 0 means “measured, and none”, NULL means “nothing to measure yet”.

Try it

For every open opportunity, show its name, its amount, and its amount after a 3% increase rounded to 2 decimal places.

Write it in the sandbox below, then press Check answer.

SQL sandbox

Run queries against the sample CRM tables (accounts, opportunities,users, contacts and opportunity_history), right in your browser. Nothing you run leaves your computer.

Back to the top of the lesson