<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. SQL
  3. Functions and expressions
All lessons in this course

Lesson 14 of 54 15 minBeginner

Numbers: integer division, rounding and decimal precision

Arithmetic on whole numbers and decimals: why 17 / 5 is 3, how ROUND, CEILING and FLOOR behave (also with negatives), what type a calculation returns, and how to avoid dividing by zero.

SQL Server does arithmetic in the types of its operands. Two ints give an int; mix in a decimal and you get a decimal with a precision and scale worked out by rules. Most surprises in calculations come from those two facts.

Integer division and remainder

Screws come in boxes of 200. How many full boxes is 1,050 screws, and how many are left over?

Query.sql
SELECT 1050 / 200 AS FullBoxes,       1050 % 200 AS LeftOver,       1050 / 200.0 AS ExactBoxes,       CEILING(1050 / 200.0) AS BoxesToBuy;
Output
FullBoxes  LeftOver  ExactBoxes  BoxesToBuy
---------  --------  ----------  ----------
        5        50    5.250000           6

With two ints, / gives the whole part and % the remainder. Writing 200.0 makes it a decimal, so the division keeps its fraction. CEILING then rounds up, because you cannot buy a quarter of a box.

Rounding

Interactive

Rounding, and what type a calculation returns

ExpressionResult
ROUND(2.5, 0)3.0
ROUND(2.5, 0, 1)2.0
CEILING(2.5)3
FLOOR(2.5)2
CAST(2.5 AS int)2
ROUND rounds a half away from zero: 2.5 becomes 3, not 2. (Some languages, including C#’s Math.Round, round halves to the even number by default.) The third argument 1 truncates instead.
FunctionDoes2.5-2.5
ROUND(x, 0)nearest; halves away from zero3.0-3.0
ROUND(x, 0, 1)cut off (truncate)2.0-2.0
CEILING(x)up, towards +∞3-2
FLOOR(x)down, towards −∞2-3
CAST(x AS int)cut off, towards zero2-2

ROUND keeps the original type, so its result still has the same number of decimal places, filled with zeros. To store or show a price with exactly two places, cast it:

Query.sql
SELECT Name, Price,       Price * 1.2 AS RawIncVat,       ROUND(Price * 1.2, 2) AS Rounded,       CAST(Price * 1.2 AS decimal(10,2)) AS IncVatFROM dbo.ProductsWHERE CategoryId = 1;
Output
Name              Price  RawIncVat  Rounded  IncVat
----------------  -----  ---------  -------  ------
Claw hammer       18.50     22.200   22.200   22.20
Cordless drill    89.00    106.800  106.800  106.80
Tape measure 8 m   9.75     11.700   11.700   11.70

What type does a calculation return?

For decimals, SQL Server works out the result’s precision (total digits) and scale (digits after the point) from the operands. Multiplying adds the scales; dividing gives at least 6 decimal places. You can ask SQL Server what it chose:

Query.sql
SELECT SQL_VARIANT_PROPERTY(CAST(10 AS decimal(10,2)) * CAST(3 AS decimal(10,2)), 'Scale') AS MultiplyScale,       SQL_VARIANT_PROPERTY(CAST(10 AS decimal(10,2)) / CAST(3 AS decimal(10,2)), 'Scale') AS DivideScale,       CAST(10 AS decimal(10,2)) / CAST(3 AS decimal(10,2)) AS Quotient;
Output
MultiplyScale  DivideScale         Quotient
-------------  -----------  ---------------
4              13           3.3333333333333

Line totals and percentages

Query.sql
SELECT OrderId, ProductId, Quantity, UnitPrice,       Quantity * UnitPrice AS LineTotal,       Quantity * 100 / 40 AS PctOf40Int,       Quantity * 100.0 / 40 AS PctOf40FROM dbo.OrderItemsWHERE OrderId = 4;
Output
OrderId  ProductId  Quantity  UnitPrice  LineTotal  PctOf40Int     PctOf40
-------  ---------  --------  ---------  ---------  ----------  ----------
      4          6        40       6.80     272.00         100  100.000000
      4          7         6      42.00     252.00          15   15.000000

Quantity * 100 / 40 is all int, so 6 × 100 / 40 = 15, not 15.0. Writing 100.0 makes the whole expression decimal. Where you put the .0 matters: Quantity * 100 / 40.0 would also work, but Quantity / 40 * 100.0 would already have lost the fraction in Quantity / 40.

Dividing by zero

Division by zero stops the whole query. NULLIF(x, 0) turns a zero divisor into NULL, and dividing by NULL simply gives NULL:

Query.sql
SELECT Name, Price, Stock,       Price / NULLIF(Stock, 0) AS PricePerUnitInStockFROM dbo.ProductsWHERE ProductId IN (8, 9, 12);
Output
Name               Price  Stock  PricePerUnitInStock
-----------------  -----  -----  -------------------
Wood screws (200)  11.90    150      0.0793333333333
Wall plugs (100)    4.50      0                 NULL
Safety goggles      6.80      0                 NULL

Other maths functions

Query.sql
SELECT ABS(-4.5) AS AbsValue,       POWER(2, 10) AS TwoToTen,       SQRT(3.0 * 3.0 + 4.0 * 4.0) AS Hypotenuse,       ROUND(PI() * POWER(0.15, 2), 4) AS ColumnAreaM2,       SIGN(-7) AS SignOfMinus7;
Output
AbsValue  TwoToTen  Hypotenuse  ColumnAreaM2  SignOfMinus7
--------  --------  ----------  ------------  ------------
     4.5      1024           5        0.0628            -1
Quick check

What is SELECT 7 / 2 * 2;?

Quick check

What is FLOOR(-2.5)?

Quick check

Which expression returns 2.5?

Practice

A builder needs 2,300 wall plugs, which come in boxes of 100 at 4.50 a box. Return the number of boxes to buy as Boxes, the total cost as Cost, and how many plugs will be spare as Spare.

Show a hint

CEILING(2300 / 100.0) gives the boxes. Multiply by the price for the cost, and subtract 2300 from boxes × 100 for the spares.

Show the solution
Solution.sql
SELECT CEILING(2300 / 100.0) AS Boxes,       CEILING(2300 / 100.0) * 4.50 AS Cost,       CEILING(2300 / 100.0) * 100 - 2300 AS Spare;
Output
Boxes    Cost  Spare
-----  ------  -----
   23  103.50      0

Remember

  • int / int is an int: 17 / 5 = 3, and % gives the remainder. Add .0 to one side to keep fractions.
  • ROUND rounds halves away from zero and keeps the scale; CEILING goes up, FLOOR goes down, CAST to int cuts off.
  • Decimal results have their own precision and scale: multiplying adds scales, dividing gives at least 6 places.
  • Cast to decimal(10,2) to store or show a value with exactly two places.
  • x / NULLIF(y, 0) avoids a divide-by-zero error.
0 of 54 lessons completed