SQL databases store raw values, but real reports rarely display them exactly as stored. Names may need to be capitalized, prices rounded, and dates formatted in a friendlier way. SQL provides built-in functions that transform values directly inside a query, so the database returns exactly what your application or report needs.
In this tutorial, you will learn how to use the most common SQL string, number, and date functions. You will see how each category works, when to use it, and how the syntax may differ between database systems.
Prerequisites
To follow along, you should be comfortable with:
- Writing basic
SELECTstatements. - Filtering rows with
WHERE. - Sorting results with
ORDER BY.
You also need a running database (PostgreSQL, MySQL, MariaDB, SQL Server, Oracle Database, or SQLite) where you can create a small sample table.
Sample Database
We will continue using the online bookstore domain from earlier tutorials. For this article, a single books table is enough.
CREATE TABLE books (
book_id INT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
author_name VARCHAR(100) NOT NULL,
price DECIMAL(8, 2) NOT NULL,
published_at DATE NOT NULL
);
INSERT INTO books (book_id, title, author_name, price, published_at) VALUES
(1, 'Clean Code', 'Robert C. Martin', 35.499, DATE '2008-08-01'),
(2, 'Effective Java', 'Joshua Bloch', 42.000, DATE '2018-01-06'),
(3, 'The Pragmatic Programmer','Andrew Hunt', 39.950, DATE '1999-10-20'),
(4, 'Refactoring', 'martin fowler', 45.750, DATE '2018-11-30'),
(5, 'Domain-Driven Design', 'Eric Evans', 50.000, DATE '2003-08-22');
Note: The literal
DATE '2008-08-01'is standard SQL. In MySQL and SQL Server you can also write'2008-08-01'directly.
What Is a SQL Function?
A function takes one or more input values and returns a single output value. Functions can appear almost anywhere a value can appear: in SELECT, WHERE, ORDER BY, and so on.
SELECT UPPER(title) AS title_upper
FROM books;
There are two broad categories worth knowing early:
- Scalar functions operate on one row at a time (the focus of this tutorial).
- Aggregate functions such as
SUM,COUNT, andAVGoperate on groups of rows and are covered in a later tutorial.
String Functions
String functions transform text values. The most commonly used ones include:
| Function | Purpose |
|---|---|
UPPER(text) |
Convert to uppercase. |
LOWER(text) |
Convert to lowercase. |
LENGTH(text) |
Return the number of characters. |
TRIM(text) |
Remove leading and trailing spaces. |
SUBSTRING(text FROM a FOR b) |
Extract part of a string. |
REPLACE(text, from, to) |
Replace occurrences of a substring. |
| Concatenation | Combine two or more strings. |
Example: Normalize Names and Show Title Length
SELECT
book_id,
UPPER(title) AS title_upper,
LOWER(author_name) AS author_lower,
LENGTH(title) AS title_length
FROM books
ORDER BY book_id;
Expected result:
| book_id | title_upper | author_lower | title_length |
|---|---|---|---|
| 1 | CLEAN CODE | robert c. martin | 10 |
| 2 | EFFECTIVE JAVA | joshua bloch | 14 |
| 3 | THE PRAGMATIC PROGRAMMER | andrew hunt | 24 |
| 4 | REFACTORING | martin fowler | 11 |
| 5 | DOMAIN-DRIVEN DESIGN | eric evans | 20 |
Example: Concatenate Values
String concatenation is one of the areas where SQL dialects differ the most.
Portable (ANSI SQL, PostgreSQL, Oracle Database, SQLite):
SELECT title || ' by ' || author_name AS display_label
FROM books;
MySQL and MariaDB:
SELECT CONCAT(title, ' by ', author_name) AS display_label
FROM books;
SQL Server:
SELECT title + ' by ' + author_name AS display_label
FROM books;
CONCAT(...) is also supported by PostgreSQL, SQL Server, and Oracle Database, and is often the safest choice when you want a single function name across systems.
Number Functions
Number functions perform math on numeric columns. Common ones include:
| Function | Purpose |
|---|---|
ABS(number) |
Absolute value. |
ROUND(number, digits) |
Round to a given number of decimals. |
CEIL(number) / CEILING(number) |
Round up to the next integer. |
FLOOR(number) |
Round down to the previous integer. |
MOD(a, b) or a % b |
Remainder after division. |
POWER(a, b) |
Raise a to the power b. |
Example: Round Prices and Apply a Discount
SELECT
book_id,
title,
price AS original_price,
ROUND(price, 2) AS price_rounded,
ROUND(price * 0.90, 2) AS price_after_10_percent_off,
CEIL(price) AS price_ceiling,
FLOOR(price) AS price_floor
FROM books
ORDER BY book_id;
Expected result:
| book_id | title | original_price | price_rounded | price_after_10_percent_off | price_ceiling | price_floor |
|---|---|---|---|---|---|---|
| 1 | Clean Code | 35.499 | 35.50 | 31.95 | 36 | 35 |
| 2 | Effective Java | 42.000 | 42.00 | 37.80 | 42 | 42 |
| 3 | The Pragmatic Programmer | 39.950 | 39.95 | 35.96 | 40 | 39 |
| 4 | Refactoring | 45.750 | 45.75 | 41.18 | 46 | 45 |
| 5 | Domain-Driven Design | 50.000 | 50.00 | 45.00 | 50 | 50 |
Notes:
CEILis calledCEILINGin SQL Server.- Rounding half-away-from-zero versus banker’s rounding may differ by database. Consult your database documentation when exact rounding rules matter, especially for money.
Date and Time Functions
Date functions extract, compute, or format temporal values. Common tasks include getting the current date, extracting a year, or computing the difference between two dates.
Frequently used functions:
| Task | PostgreSQL / ANSI | MySQL | SQL Server |
|---|---|---|---|
| Current date | CURRENT_DATE |
CURDATE() |
CAST(GETDATE() AS DATE) |
| Current timestamp | CURRENT_TIMESTAMP |
NOW() |
GETDATE() / SYSDATETIME() |
| Extract year | EXTRACT(YEAR FROM published_at) |
YEAR(published_at) |
YEAR(published_at) |
| Extract month | EXTRACT(MONTH FROM published_at) |
MONTH(published_at) |
MONTH(published_at) |
| Add days | published_at + INTERVAL '7 days' |
DATE_ADD(published_at, INTERVAL 7 DAY) |
DATEADD(day, 7, published_at) |
| Difference in days | (a - b) |
DATEDIFF(a, b) |
DATEDIFF(day, b, a) |
Tip:
EXTRACTis defined by the SQL standard and works in PostgreSQL, MySQL 8+, MariaDB, and Oracle Database. Prefer it when portability matters.
Example: Show Publication Year and Age in Years
Portable version using EXTRACT:
SELECT
book_id,
title,
published_at,
EXTRACT(YEAR FROM published_at) AS published_year,
EXTRACT(YEAR FROM CURRENT_DATE)
- EXTRACT(YEAR FROM published_at) AS age_in_years
FROM books
ORDER BY published_at;
Expected result (as of 2026):
| book_id | title | published_at | published_year | age_in_years |
|---|---|---|---|---|
| 3 | The Pragmatic Programmer | 1999-10-20 | 1999 | 27 |
| 5 | Domain-Driven Design | 2003-08-22 | 2003 | 23 |
| 1 | Clean Code | 2008-08-01 | 2008 | 18 |
| 2 | Effective Java | 2018-01-06 | 2018 | 8 |
| 4 | Refactoring | 2018-11-30 | 2018 | 8 |
Note:
age_in_yearscomputed by subtracting years is an approximation. If a book was published later in the year than today’s date, the true age is one year less. Precise age calculations require additional logic and are covered in an intermediate tutorial.
Example: Filter Books Published in the Last 10 Years
PostgreSQL:
SELECT title, published_at
FROM books
WHERE published_at >= CURRENT_DATE - INTERVAL '10 years'
ORDER BY published_at DESC;
MySQL:
SELECT title, published_at
FROM books
WHERE published_at >= DATE_SUB(CURDATE(), INTERVAL 10 YEAR)
ORDER BY published_at DESC;
SQL Server:
SELECT title, published_at
FROM books
WHERE published_at >= DATEADD(YEAR, -10, CAST(GETDATE() AS DATE))
ORDER BY published_at DESC;
Combining Functions
Functions can be nested to build more expressive queries. Combining string and date functions is a common pattern for building readable labels.
PostgreSQL / Oracle Database / SQLite:
SELECT
UPPER(TRIM(title))
|| ' ('
|| CAST(EXTRACT(YEAR FROM published_at) AS VARCHAR(4))
|| ')' AS display_label
FROM books
ORDER BY published_at;
MySQL:
SELECT
CONCAT(UPPER(TRIM(title)), ' (', YEAR(published_at), ')') AS display_label
FROM books
ORDER BY published_at;
Expected result (values are the same regardless of dialect):
| display_label |
|---|
| THE PRAGMATIC PROGRAMMER (1999) |
| DOMAIN-DRIVEN DESIGN (2003) |
| CLEAN CODE (2008) |
| EFFECTIVE JAVA (2018) |
| REFACTORING (2018) |
Common Mistakes
Assuming a Function Exists in Every Database
Not every database supports the same function name. For example, LEN exists in SQL Server, but the portable equivalent is LENGTH (or CHAR_LENGTH for character counts on multibyte strings). Always check the reference for your database version before assuming a function is available.
Applying a Function to an Indexed Column in WHERE
Wrapping an indexed column in a function often prevents the database from using its index efficiently.
Slower:
SELECT title
FROM books
WHERE YEAR(published_at) = 2018;
Usually faster and index-friendly:
SELECT title
FROM books
WHERE published_at >= DATE '2018-01-01'
AND published_at < DATE '2019-01-01';
Measure with EXPLAIN before assuming one form is always faster; execution-plan syntax and output vary between database systems.
Comparing Strings with Inconsistent Case
'Java' and 'java' may or may not compare as equal, depending on the database and the column’s collation. When case-insensitive comparisons are required, normalize both sides:
SELECT title
FROM books
WHERE LOWER(author_name) = LOWER('Martin Fowler');
Be aware that this may still bypass indexes; consider using a case-insensitive collation or a functional index when supported.
Confusing NULL Behavior
Most functions return NULL when given a NULL argument. For example, LENGTH(NULL) returns NULL, not 0. Use COALESCE to substitute a default:
SELECT COALESCE(LENGTH(author_name), 0) AS name_length
FROM books;
Database Compatibility
- PostgreSQL: Rich set of standard-compliant functions. Supports
||concatenation,EXTRACT, andINTERVALarithmetic. - MySQL / MariaDB: Provide
CONCAT,YEAR,MONTH,DATE_ADD,DATE_SUB,NOW,CURDATE. Note that||is logical OR unlessPIPES_AS_CONCATmode is enabled. - SQL Server: Uses
+for string concatenation,LENfor string length,GETDATE,DATEADD,DATEDIFF. Also supportsCONCAT. - Oracle Database: Supports
||concatenation,SUBSTR,LENGTH,TO_CHAR,ADD_MONTHS, andSYSDATE. - SQLite: Provides a smaller set of functions. Dates are typically stored as text or numbers and manipulated with
DATE,STRFTIME, andDATETIME.
When in doubt, check your database version’s official reference.
Best Practices
- Prefer standard functions such as
EXTRACTandCONCATwhen they exist in your target databases. - Avoid wrapping indexed columns in functions inside
WHEREwhen equivalent range predicates exist. - Format values in the application layer when possible; use SQL functions when the transformation belongs to the query result (for example, aggregation keys or grouping).
- Keep expressions readable: alias every derived column with
AS. - Be explicit about types when combining functions, especially when mixing strings, numbers, and dates.
Conclusion
You learned how to use the most common SQL string, number, and date functions to transform values directly inside a query, and how their syntax varies between PostgreSQL, MySQL, SQL Server, Oracle Database, and SQLite. Use these functions to shape query results into exactly the form your application or report needs, and remember that applying functions to indexed columns can affect performance.
