Step 1 Understand it
Why you need it. Data rarely arrives in the shape you need: names in mixed case, prices that need rounding, dates you want as "2024-03" or as "how many days ago". Functions do these small transformations inside the .
Watch it happen
Read the animation as text
- A function takes a value and gives back one value — once for every row. Two rows in department 2: Dara and Eli.
- UPPER('Dara') = 'DARA'. Worked out for each row separately.
- LENGTH counts characters: 'Dara' has 4.
- SUBSTR(name, 1, 3): start at character 1 (SQL counts from 1), take 3: 'Dar'.
- Two rows in, two rows out. That is the difference from an aggregate like SUM, which turns many rows into one (Phase 2).
Picture it
flowchart LR A["one value per row<br/>Dara · 52000 · 2024-01-21"] --> F["a function<br/>UPPER · ROUND · strftime"] --> B["one new value per row<br/>DARA · 4333.33 · 2024-01"]
The idea
A function takes values in brackets and returns one value. Text: and LOWER change case, LENGTH counts characters, SUBSTR(text, start, length) cuts out a piece (counting from 1), TRIM removes spaces at the ends, and REPLACE swaps text. Numbers: ROUND(x, 2) rounds to 2 decimal places, ABS removes the minus sign. Dates are where differ most: uses and julianday; uses DATE_TRUNC, EXTRACT and date subtraction. These run once per row — unlike aggregate functions, which turn many rows into one (Phase 2).
The shape of it
UPPER(text) LENGTH(text) SUBSTR(text, start, length)
ROUND(number, places)
strftime('%Y-%m', date) -- SQLite: the year and monthWords in lower case are the parts you fill in.
How it works, step by step
UPPER('Dara')is 'DARA';LENGTH('Dara')is 4;SUBSTR('Dara', 1, 3)is 'Dar' (start at character 1, take 3).- salary / 12.0 gives a long decimal;
ROUND(…, 2)keeps 2 decimal places. strftime('%Y-%m', '2024-01-21')is '2024-01': the year and month, ready to group by.- julianday turns a date into a day number, so subtracting two of them gives the days between.
- Functions can be nested:
UPPER(SUBSTR(name, 1, 1))is the first letter in capitals.
Analogy
Kitchen tools. A peeler (TRIM), a slicer (SUBSTR), a measuring spoon (). Each one takes one ingredient and gives back a changed version of it.
Where the analogy breaks
Kitchen tools are the same in every kitchen. Date functions are not the same in every database — so in an interview, say which database you are assuming, and the interviewer will usually accept any reasonable syntax.
Worked examples
Text functions
SELECT name, UPPER(name) AS shout, LENGTH(name) AS letters, SUBSTR(name, 1, 3) AS first_three
FROM employees
WHERE dept_id = 2
ORDER BY name;2 rows
| name | shout | letters | first_three |
|---|---|---|---|
| Dara | DARA | 4 | Dar |
| Eli | ELI | 3 | Eli |
Rounding
SELECT name, salary, ROUND(salary / 12.0, 2) AS monthly
FROM employees
WHERE dept_id = 3 OR dept_id IS NULL
ORDER BY name;2 rows
| name | salary | monthly |
|---|---|---|
| Fatima | 52000 | 4333.33 |
| Gus | 38000 | 3166.67 |
Dividing by 12.0, not 12, avoids . ROUND then keeps two decimal places.
Dates: the month, and days between
SELECT order_id, order_date,
strftime('%Y-%m', order_date) AS month,
julianday('2024-03-31') - julianday(order_date) AS days_before_end_of_march
FROM orders
WHERE customer_id = 2
ORDER BY order_id;2 rows
| order_id | order_date | month | days_before_end_of_march |
|---|---|---|---|
| 104 | 2024-01-21 | 2024-01 | 70 |
| 108 | 2024-03-01 | 2024-03 | 30 |
This is SQLite's way. The notes below give the PostgreSQL, and versions.
Must-know points
- A row function runs once per row and returns one value; an aggregate function squashes many rows into one.
- Text positions start at 1, not 0:
SUBSTR('Dara', 1, 3)is 'Dar'. ROUND(x, 2)for display; divide by a decimal (12.0) to avoid integer division first.- Date functions differ the most between databases. Know your target database's month-truncation and date-difference functions.
- Wrapping a in a function inside (
WHERE UPPER(name) = …) stops a normal on that column being used.
Common mistakes
- Counting text positions from 0, as in Python.
- Rounding before summing, which adds up rounding errors. Sum first, round at the end.
- Comparing dates stored as '06/01/2024' text — they compare as text, not as dates.
In other databases
- Month of a date: PostgreSQL
DATE_TRUNC('month', d)orTO_CHAR(d, 'YYYY-MM'); MySQLDATE_FORMAT(d, '%Y-%m'); SQL ServerFORMAT(d, 'yyyy-MM'); SQLitestrftime('%Y-%m', d). - Days between: PostgreSQL
d2 - d1(dates); MySQLDATEDIFF(d2, d1); SQL ServerDATEDIFF(day, d1, d2); SQLitejulianday(d2) - julianday(d1). - Year of a date:
EXTRACT(YEAR FROM d)in PostgreSQL and MySQL;YEAR(d)in MySQL and SQL Server;strftime('%Y', d)in SQLite. - Text length is
LENGTHin most databases butLENin SQL Server.
Step 2 Go further (free, optional)
Optional. The notes above are written to be enough on their own. These free resources give a second explanation and more practice.
-
Read
SQLite docs — Date And Time Functions
official documentation, free
-
Read
PostgreSQL docs — Date/Time Functions and Operators
official documentation, free
-
Practice
SQLZoo — SELECT from WORLD: string functions
free exercises
Step 3 Practise
Try each one yourself first: out loud, or written and run in the practice box below, which saves every query you run. Then open the answer and compare.
-
ExplainSay what it does and when to use it, in plain words, with no notes.
Explain the difference between a row function like UPPER and an aggregate like .
Show answer
A row function such as UPPER works on one value from one row and gives one value back. 8 employees in, 8 rows out, each with its own upper-case name.
An aggregate such as SUM reads a whole column across many rows and gives back one value for the group. 8 salaries in, one total out. The rows collapse.
That is why you can write
SELECT name, UPPER(name), but notSELECT name, SUM(salary)without : one name per row can't sit next to one total for all rows. -
Writefrom a blank page, then run it
Write a query that shows each customer's name and the part of their email before the @. (Hint:
INSTR(email, '@')gives the position of the @.)Show answer
INSTR(email, '@')finds where the @ is. Take the characters from position 1 up to one before it. For priya@mail.com the @ is at position 6, so take 5 characters.sqlSELECT name, email, SUBSTR(email, 1, INSTR(email, '@') - 1) AS username FROM customers ORDER BY customer_id;6 rows
name email username Priya priya@mail.com priya Tom tom@mail.com tom Lena lena@mail.com lena Omar omar@mail.com omar Zoe zoe@mail.com zoe Tom tom@mail.com tom -
Predictsay it before you run it
What does
SELECT SUBSTR('Headphones', 5, 5), LENGTH(TRIM(' Pen ')), ROUND(2.675, 1);return?Show answer
'phone', 3 and 2.7.
•
SUBSTR('Headphones', 5, 5): start at character 5 (counting from 1: H-e-a-d-p, so it starts at the p) and take 5 characters: "phone".
•TRIMremoves the spaces at both ends, leaving "Pen", which has 3 letters.
•ROUND(2.675, 1)keeps one decimal place: 2.7.sqlSELECT SUBSTR('Headphones', 5, 5), LENGTH(TRIM(' Pen ')), ROUND(2.675, 1);1 row
SUBSTR('Headphones', 5, 5) LENGTH(TRIM(' Pen ')) ROUND(2.675, 1) phone 3 2.7 -
Edge casesSay what happens with NULLs, ties, duplicates or no rows at all, and fix it.
How many days passed between each employee's hire_date and '2024-01-01'? What happens if one of the dates is ?
Show answer
Subtract the julianday numbers: each date becomes a count of days, so the difference is days between. Asha had been there 1,767 days by 1 January 2024.
If either date is NULL, julianday gives NULL, and the subtraction gives NULL for that row. There is no error, just a blank. Decide what you want: drop those rows with
WHERE hire_date IS NOT NULL, or show a default with . (In SQLite,SELECT julianday('2024-01-01') - julianday(NULL);returns NULL.)sqlSELECT name, hire_date, julianday('2024-01-01') - julianday(hire_date) AS days_to_2024 FROM employees ORDER BY emp_id;8 rows
name hire_date days_to_2024 Asha 2019-03-01 1767 Ben 2020-06-15 1295 Chen 2021-01-10 1086 Dara 2020-02-20 1411 Eli 2022-07-01 549 Fatima 2021-09-12 841 Gus 2023-04-03 273 Hana 2022-11-30 397
Practice box: write and run your answers here
Your queries in this box
Step 4 Interview-ready, then prove it
Your interview answer
About 20 to 40 seconds, said out loud.
Scalar functions transform one value per row: functions like UPPER, SUBSTRING and TRIM, numeric ones like ROUND, and date functions for truncating to a month or finding the days between two dates. Date syntax is the least portable part of , so in an interview I name the dialect I am assuming — usually PostgreSQL, with DATE_TRUNC and date subtraction — and I avoid wrapping indexed columns in functions inside WHERE, because that blocks the index.
Follow-up questions
What is the difference between a scalar function and an aggregate function?
A scalar function returns one value per row (UPPER, ROUND). An aggregate returns one value per group of rows (SUM, ).
How do you group orders by month in PostgreSQL?
GROUP BY DATE_TRUNC('month', order_date), which turns every date into the first day of its month.
Tick each check when you can do it without notes
A topic is finished when you pass all four checks, not when you have read it. Be strict: if you had to look at the notes, it is not ticked yet.
- Explain Say what it does and when to use it, in plain words, with no notes.
- Write Write the query from a blank page, then run it to check.
- Predict Read a query and say which rows it returns before you run it.
- Edge cases Say what happens with NULLs, ties, duplicates or no rows at all, and fix it.
0 of 4 checks ticked.
Saved in this browser. Back it up or move it to another device.