Skip to content
wannacode

Arithmetic in a query

In short: In SELECT you can calculate with columns (+, -, *, /) for every row and round with ROUND, but in SQLite, PostgreSQL and SQL Server dividing two integers drops the fractional part.

Problem

What does this line cost?

Accounting wants to know, for every order line, what it costs: the quantity times the price. The order_items table has only the two numbers:

SQLdatabase: shop

Order 3 has apples, 4 at 2.10 €, and laundry powder, 3 at 12.50 €. Working it out by hand for 152 lines is not a job for a person. And keeping the total in yet another column means keeping it correct after every change.

The idea

A column that is computed

Besides columns, SELECT can contain expressions: calculations with the columns. The database computes them for every row:

SQLdatabase: shop

The operators are +, -, *, / and % (the remainder of a division). The computed column is named after the expression itself, so you give it a short alias with AS. The table does not change: the calculation exists only in the result.

You can also sort by the alias. The five most expensive lines across all orders:

SQLdatabase: shop

An expression works without a table, too. Then SELECT computes it once, like a calculator. Multiplication comes before addition, and brackets change the order:

SQLdatabase: shop

Try it

The price with VAT

The prices in the database are without VAT. Show the name, the price and the price with 20% VAT in a column called with_vat.

SQLdatabase: shop

See it

The rating as a percentage

The website wants to show the ratings from 1 to 5 as percentages: 5 is 100%, 4 is 80%. Run:

SQLdatabase: shop
1. The review rated 4 came out as 0%. Why?

This is integer division. When both numbers are whole, the result is whole as well:

SQLdatabase: shop

As soon as one of the numbers is a fraction (2.0), or is turned into one with CAST(… AS REAL), the division keeps the fraction. For the percentages it is enough to multiply first, by 100.0:

SQLdatabase: shop

With price this trap does not happen: the price is a fractional number (REAL). Watch out for columns holding whole numbers, like rating, quantity and stock.

More precisely, if you are curious

PostgreSQL and SQL Server divide whole numbers the same way: 5 / 2 is 2. MySQL does the opposite: there 5 / 2 is 2.5000, and integer division has its own operator, DIV. Division by zero gives NULL in SQLite and an error in PostgreSQL.

The idea

ROUND: to the cent

A promotion: 15% off dairy. The new price is 85% of the old one:

SQLdatabase: shop

Nobody can pay 0.935 € for a yoghurt. ROUND(x, 2) rounds to two digits after the point, that is to the cent. ROUND(x) rounds to a whole number.

Look at the white cheese: 6.715 became 6.71, not 6.72. To see why, look below.

More precisely, if you are curious

Fractional numbers in a computer are stored in binary, and in binary 0.1 has no exact form, just as 1/3 has no exact form in decimal digits. That is why:

SQLdatabase: shop

The screen shows 0.3, but the comparison says “no” (0). printf with %!.20f shows what is really stored. The white cheese is the same story: 7.9 × 0.85 is 6.71499… in memory, and ROUND honestly rounds it down.

That is why real systems do not keep money as REAL. They keep it in whole cents (1.20 € is 120), or in a type for exact decimal numbers: NUMERIC(10, 2) or DECIMAL(10, 2) in PostgreSQL, MySQL and SQL Server. SQLite has no such type, so cents are the way to go there:

SQLdatabase: shop

In cents, 790 × 85 is exactly 67,150, and dividing by 100 gives 671.5 cents, which round to 672, that is 6.72 €.

Try it

The promotion

Show the name, the price and the sale price of every product. The sale price is price * 0.85, rounded to the cent, in a column called sale_price.

SQLdatabase: shop

Exercise

Two tasks

1. The insurer asks what the goods in the warehouse are worth. Show the five products with the largest stock value (price times stock), largest first. The gift card is not goods on a shelf: keep only the products that have a category.

Name, stock and stock_value, the five largest:

SQLdatabase: shop

2. Goods arrive in boxes of 12. For every product with a category that is in stock, show how many full boxes there are (boxes) and how many items are left outside them (loose).

Name, stock, boxes and loose:

SQLdatabase: shop

Test

Check yourself

1. What does SELECT 7 / 2; return?
2. Does SELECT price * 1.2 AS with_vat FROM products; change the prices in the table?
3. Which one sorts the order lines by value, largest first?
4. What does ROUND(2.345, 1) give?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·