The data stored in a table isn't always in the shape you want to show. Names might need capital letters, a long description might need shortening, prices might need rounding and dates might need just the year. Rather than fixing all of that in another program, you can transform values inside the query with functions.
You've already met a few functions: ROUND() in the SELECT lesson and COUNT() and SUM() in the ORDER BY and GROUP BY lesson. Those last two are aggregate functions that summarise many rows. This lesson covers scalar functions, which work on one value at a time and return one value for every row.
Every database system supports the common functions in this lesson, though a few of the names differ. The examples use SQLite, and each section notes the MySQL and PostgreSQL spelling where it's different, so you'll be ready for whichever database you meet first.
Text functions
| Function | Example | Result |
|---|---|---|
UPPER(text) | UPPER('leeds') | LEEDS |
LOWER(text) | LOWER('LEEDS') | leeds |
LENGTH(text) | LENGTH('Chloe') | 5 |
SUBSTR(text, start, length) | SUBSTR('Backpack', 1, 4) | Back |
TRIM(text) | TRIM(' Ana ') | Ana |
REPLACE(text, from, to) | REPLACE('Desk lamp', ' ', '-') | Desk-lamp |
SQL counts characters from 1, not 0, so SUBSTR('Backpack', 1, 4) starts at the very first letter. To join pieces of text, standard SQL uses ||: name || ' from ' || city. MySQL uses CONCAT(name, ' from ', city) instead, and PostgreSQL accepts both.
Number functions
ROUND(x, 2)rounds to two decimal places;ROUND(x)rounds to a whole number.ABS(x)gives the distance from zero, soABS(-7)is7.CEIL(x)andFLOOR(x)round up and down. (SQLite added these in version 3.35.)- The usual arithmetic operators,
+ - * /, work on columns too.
Watch out for integer division: in SQLite and PostgreSQL, dividing one whole number by another throws away the remainder, so 7 / 2 is 3. Multiply by 1.0 first, 7 * 1.0 / 2, to get 3.5. MySQL returns a decimal automatically.
Date functions
Dates are where databases differ most. To get the year of the joined column:
| Database | Year | Today's date |
|---|---|---|
| SQLite | strftime('%Y', joined) | date('now') |
| MySQL | YEAR(joined) | CURDATE() |
| PostgreSQL | EXTRACT(YEAR FROM joined) | CURRENT_DATE |
Storing dates in the standard YYYY-MM-DD format is what makes them sort and compare correctly, even in SQLite where they're stored as text. Date arithmetic also differs: SQLite uses date(joined, '+30 days'), MySQL uses DATE_ADD(joined, INTERVAL 30 DAY) and PostgreSQL uses joined + INTERVAL '30 days'.
Handling NULL with COALESCE and NULLIF
NULL means "no value", and almost any calculation involving NULL gives NULL. Two small functions make NULLs easier to handle:
COALESCE(a, b, c)returns the first value that isn't NULL.COALESCE(phone, 'not given')shows a friendly label instead of an empty cell.NULLIF(a, b)returns NULL if a equals b, otherwise a. Its classic use is avoiding division by zero:total / NULLIF(count, 0)gives NULL instead of an error when count is 0.
Both functions are part of standard SQL and work the same way in every major database.
SQL Functions Syntax
Common functions
SELECT UPPER(name), LENGTH(name), SUBSTR(name, 1, 3),
name || ' (' || city || ')',
ROUND(price * 1.2, 2),
COALESCE(phone, 'n/a'),
total / NULLIF(qty, 0)
FROM table_name;SQL Functions Summary
- Scalar functions transform one value per row; aggregate functions summarise many rows.
- Text:
UPPER,LOWER,LENGTH,SUBSTR,TRIM,REPLACE; join text with||orCONCAT. - Numbers:
ROUND,ABS,CEIL,FLOOR; beware of integer division. - Date functions differ between SQLite, MySQL and PostgreSQL.
COALESCEreplaces NULLs with a fallback;NULLIFturns a value into NULL.
SQL Functions Examples
Each example shows the code, the output it produces and what each part does.
Formatting customer names and cities
SELECT name,
UPPER(city) AS city,
LENGTH(name) AS letters,
name || ' from ' || city AS label
FROM customers;name | city | letters | label
------+--------+---------+------------------
Ana | LISBON | 3 | Ana from Lisbon
Ben | LEEDS | 3 | Ben from Leeds
Chloe | LYON | 5 | Chloe from Lyon
Dev | LEEDS | 3 | Dev from Leeds
Ema | PORTO | 3 | Ema from PortoLine-by-line explanation
- Line 2UPPER() changes only the result, not the stored data.
- Line 3LENGTH() counts the characters in each name.
- Line 4|| joins text together. In MySQL, write CONCAT(name, ' from ', city).
Building product codes
SELECT UPPER(SUBSTR(category, 1, 3)) || '-' || id AS code,
name,
ROUND(price * 0.9, 2) AS sale_price
FROM products
ORDER BY code;code | name | sale_price
------+--------------+-----------
BAG-3 | Backpack | 31.49
HOM-6 | Desk lamp | 22.05
KIT-4 | Water bottle | 10.8
KIT-5 | Lunch box | 7.88
STA-1 | Notebook | 3.15
STA-2 | Pen | 1.08Line-by-line explanation
- Line 1The first three letters of the category in capitals, a dash, then the id.
- Line 3A 10 percent discount, rounded to two decimals.
- Line 5You can sort by an alias defined in the SELECT list.
Working with dates
SELECT name,
joined,
strftime('%Y', joined) AS year_joined,
date(joined, '+1 year') AS first_anniversary
FROM customers
WHERE joined >= '2024-01-01';name | joined | year_joined | first_anniversary
------+------------+-------------+------------------
Chloe | 2024-01-03 | 2024 | 2025-01-03
Dev | 2024-03-15 | 2024 | 2025-03-15
Ema | 2024-06-30 | 2024 | 2025-06-30Line-by-line explanation
- Line 3strftime('%Y', ...) pulls out the year in SQLite. MySQL: YEAR(joined).
- Line 4date(..., '+1 year') adds a year. MySQL: DATE_ADD(joined, INTERVAL 1 YEAR).
- Line 6ISO dates compare correctly as text, so this finds everyone who joined in 2024.
Common SQL Functions Mistakes
Counting from 0 in SUBSTR
SELECT SUBSTR('Backpack', 0, 4); -- 'Bac' in SQLiteSELECT SUBSTR('Backpack', 1, 4); -- 'Back'SQL positions start at 1. Starting at 0 gives a confusing, shorter result.
Joining text with + in standard SQL
SELECT name + ' from ' + city FROM customers;SELECT name || ' from ' || city FROM customers;+ is for numbers. It only joins text in SQL Server; MySQL uses CONCAT().
Dividing whole numbers
SELECT 7 / 2; -- 3 in SQLite and PostgreSQLSELECT 7 * 1.0 / 2; -- 3.5SQL Functions in Real Projects
Functions in reporting
Reports almost always reshape data on the way out: names in proper case, prices rounded to two decimals, dates grouped by month with strftime('%Y-%m', order_date) and missing values shown as "n/a" with COALESCE. Doing this inside the query keeps the application code simple and makes the same report easy to reuse in a spreadsheet or a dashboard.
SQL Functions FAQ
How do I join text in MySQL, PostgreSQL and SQLite?
PostgreSQL and SQLite use the standard || operator: name || ' (' || city || ')'. MySQL treats || as logical OR by default, so use CONCAT(name, ' (', city, ')') there; PostgreSQL also supports CONCAT. A difference to remember is that || with a NULL gives NULL, while CONCAT in PostgreSQL skips NULL arguments.
Why does ROUND give different results in different databases?
For exact decimal types, most databases round halves away from zero, so ROUND(2.5) is 3. For floating-point values, some use "round half to even": in PostgreSQL, round(2.5::numeric) is 3 but round(2.5::float8) is 2. Store money as DECIMAL to get predictable rounding.
Does LENGTH count characters or bytes?
In SQLite and PostgreSQL, LENGTH counts characters, so LENGTH('café') is 4. In MySQL, LENGTH counts bytes and returns 5, because é uses two bytes in UTF-8; use CHAR_LENGTH there for characters. PostgreSQL's OCTET_LENGTH gives the byte count.
Do functions in WHERE make queries slower?
They can. WHERE UPPER(city) = 'LEEDS' or WHERE strftime('%Y', joined) = '2024' must calculate the function for every row, so an ordinary index on the column can't be used. Compare the raw column with a range where possible, or create an index on the expression, as the indexes lesson explains.
Can I write my own SQL functions?
Yes, in most server databases. PostgreSQL and MySQL support CREATE FUNCTION, and SQL Server and Oracle have their own versions; SQLite lets the host program register functions instead. The triggers and procedures lesson introduces code that lives inside the database.
SQL Functions Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the query so every city is shown in capital letters.
SELECT (city) FROM customers;
What does this return?
SELECT LENGTH('Water bottle');
What does this return?
SELECT COALESCE(NULL, NULL, 'none', 'other');
Which expression avoids a division-by-zero error when qty is 0?
What does this return?
SELECT SUBSTR('Notebook', 5, 4);
SQL Functions Quiz
5 questions to check what you learned.
What kind of function returns one value for each row?
- An aggregate function
- A window function
- A scalar function
- A table function
What does
ROUND(8.756, 1)return?- 8.7
- 8.8
- 9
- 8.75
How do you join two pieces of text in standard SQL?
- With +
- With &
- With .
- With ||
What does
COALESCE(phone, 'n/a')show when phone is NULL?- n/a
- NULL
- An empty string
- An error
Why can
WHERE LOWER(email) = 'a@b.com'be slow on a big table?- LOWER is deprecated
- Text comparisons are always slow
- The function runs on every row, so a normal index on email can't be used
- It returns duplicate rows