Skip to content
Functions

SQL String Functions: UPPER, SUBSTRING, TRIM & More

7 min readUpdated September 24, 2026Every example verified
On this page 11 sections ▾
  1. Changing case: UPPER and LOWER
  2. Measuring length: LENGTH and LEN
  3. Slicing text: SUBSTRING
  4. Find and replace: REPLACE
  5. Trimming whitespace: TRIM
  6. Joining text: || and CONCAT
  7. Handling missing text: COALESCE and NULLIF
  8. One habit worth building
  9. Practice this topic
  10. Frequently asked questions
  11. Related reading

Text arrives messy: inconsistent capitalisation, stray spaces, values glued together that you need apart, values apart that you need together. String functions are SQL's toolkit for cleaning and reshaping text inside the query, so the data comes back the way the report needs it. This guide covers the core set that exists, under one spelling or another, in every major database.

Changing case: UPPER and LOWER

SELECT UPPER(first_name) AS shouted, LOWER(email) AS normalised
FROM Employees
ORDER BY emp_id
LIMIT 3;
shoutednormalised
ANANYAananya.rao@example.com
MARCUSm.bennett@example.com
LEILAleila.h@example.com

● 3 rows · produced by running this query on the sample database

Cosmetics are the obvious use; the important one is case-insensitive comparison. Whether 'Austin' equals 'austin' depends on the database's collation settings — in SQLite, which runs this playground, it does not — but lower-casing both sides makes the comparison behave the same everywhere:

SELECT company, city
FROM Customers
WHERE LOWER(city) = 'austin';

Measuring length: LENGTH and LEN

SELECT first_name, LENGTH(first_name) AS name_length
FROM Employees
ORDER BY name_length DESC, first_name
LIMIT 5;
first_namename_length
Hiroshi7
Isabela7
Ananya6
Daniel6
Marcus6

● 5 rows · produced by running this query on the sample database

Hiroshi and Isabela tie at seven characters and three names tie at six, so first_name breaks the ties and keeps the order the same on every run. Typical uses are data quality checks — finding suspiciously short names, or values that overflow a field limit.

The spelling is the annoying part. SQLite, MySQL and PostgreSQL say LENGTH, and this playground, which runs SQLite, accepts only that. SQL Server calls it LEN:

-- SQL Server
SELECT TOP 5 first_name, LEN(first_name) AS name_length
FROM Employees
ORDER BY name_length DESC, first_name;

The behaviour is not quite identical either. MySQL’s LENGTH counts bytes, so a letter such as é counts as two; CHAR_LENGTH counts characters there. SQL Server’s LEN ignores trailing spaces, so LEN('abc ') is 3, while SQLite’s LENGTH('abc ') is 5.

Slicing text: SUBSTRING

SELECT email, SUBSTRING(email, 1, 5) AS first_five
FROM Employees
ORDER BY emp_id
LIMIT 3;
emailfirst_five
ananya.rao@example.comanany
m.bennett@example.comm.ben
leila.h@example.comleila

● 3 rows · produced by running this query on the sample database

The arguments are the string, the starting position, and how many characters to take — and SQL counts from 1, not 0, which surprises everyone arriving from a programming language. Combined with other functions it builds things like initials:

SELECT company, UPPER(SUBSTRING(company, 1, 1)) AS initial
FROM Customers
ORDER BY customer_id
LIMIT 5;
companyinitial
Meridian Trading Co.M
Acme RoboticsA
Sakura LogisticsS
Kilimanjaro FoodsK
Andes TextilesA

● 5 rows · produced by running this query on the sample database

Find and replace: REPLACE

SELECT REPLACE(email, '@example.com', '') AS mailbox
FROM Employees
ORDER BY emp_id
LIMIT 4;
mailbox
ananya.rao
m.bennett
leila.h
t.nowak

● 4 rows · produced by running this query on the sample database

REPLACE swaps every occurrence of one substring for another — here replacing the domain with nothing, which strips it. It only changes the query's output; the stored data is untouched unless you put the expression inside an UPDATE.

Trimming whitespace: TRIM

SELECT TRIM('   SQL Practice   ') AS trimmed;
trimmed
SQL Practice

● 1 row · produced by running this query on the sample database

TRIM removes leading and trailing spaces — the classic cure for data pasted in from spreadsheets, where 'Sales ' quietly fails to equal 'Sales'. Most databases also offer LTRIM and RTRIM for one side only.

Joining text: || and CONCAT

SELECT first_name || ' ' || last_name AS full_name,
       CONCAT(last_name, ', ', first_name) AS sorted_name
FROM Employees
ORDER BY emp_id
LIMIT 3;
full_namesorted_name
Ananya RaoRao, Ananya
Marcus BennettBennett, Marcus
Leila HaddadHaddad, Leila

● 3 rows · produced by running this query on the sample database

Both forms work here, which is convenient because the real world splits: || is the SQL standard (PostgreSQL, SQLite, Oracle), while MySQL wants CONCAT() and SQL Server uses + or, since SQL Server 2012, CONCAT(). SQLite gained CONCAT() in version 3.44. The difference that bites is what happens when one part is NULL:

SELECT 'Ananya' || NULL AS with_pipes,
       CONCAT('Ananya', NULL) AS with_concat,
       'Ananya' || COALESCE(NULL, '') AS pipes_made_safe;
with_pipeswith_concatpipes_made_safe
NULLAnanyaAnanya

● 1 row · produced by running this query on the sample database

In this playground, as in PostgreSQL, a NULL anywhere in a || chain makes the whole result NULL, while CONCAT() skips it. That is not universal: MySQL’s CONCAT() returns NULL if any argument is NULL (its CONCAT_WS() skips them), SQL Server’s + returns NULL by default just like ||, and Oracle’s || treats NULL as empty text. The third column is the portable fix: wrap any part that may be missing in COALESCE(part, ''), and whichever way your database joins text, the answer is the same.

Handling missing text: COALESCE and NULLIF

Not strictly string functions — they work on any type — but text is where you meet them daily. COALESCE returns the first non-NULL argument, which is how a NULL becomes a readable label:

SELECT e.first_name, COALESCE(m.first_name, 'no manager') AS reports_to
FROM Employees e
LEFT JOIN Employees m ON m.emp_id = e.manager_id
ORDER BY e.emp_id
LIMIT 6;
first_namereports_to
Ananyano manager
MarcusAnanya
LeilaAnanya
Tomasno manager
GraceTomas
HiroshiTomas

● 6 rows · produced by running this query on the sample database

NULLIF is its mirror: NULLIF(a, b) returns NULL when the two arguments are equal, otherwise returns the first. Its everyday job is turning junk placeholder values — empty strings, 'N/A' — back into honest NULLs before COALESCE or aggregates handle them:

SELECT NULLIF('same', 'same') AS becomes_null, NULLIF('a', 'b') AS stays_a;
becomes_nullstays_a
NULLa

● 1 row · produced by running this query on the sample database

Try it yourself

Every example above marked runs in the playground works on the sample database as-is. Try chaining functions — UPPER inside SUBSTRING inside REPLACE — and watch how they compose.

Open the Playground

One habit worth building

Wrapping a column in a function inside WHERE — as in LOWER(city) = 'austin' — is fine on small data, but on a large indexed table it can stop the database using the index, because the index stores city, not LOWER(city). Production databases solve this with case-insensitive collations or function-based indexes. Nothing to act on while learning — just remember that where you put the function matters more as tables grow.

Practice this topic

Reading explains it; solving it is what makes it stick. These exercises use exactly what this page covered, and your answer is checked by running it against the same database:

See all 42 exercises →

Frequently asked questions

What are string functions in SQL?

Built-in functions that transform text values inside a query: changing case, measuring length, slicing out substrings, replacing text, trimming spaces and joining values together. They change what the query returns, not what the table stores.

What is the difference between LEN and LENGTH?

Mostly the spelling: SQL Server uses LEN; MySQL, PostgreSQL and SQLite use LENGTH. Two traps hide behind it. MySQL's LENGTH counts bytes, so a letter such as é counts as two; use CHAR_LENGTH there to count characters. SQL Server's LEN ignores trailing spaces. SQLite, which runs this site's playground, accepts only LENGTH.

Does SUBSTRING start counting at 0 or 1?

At 1. SUBSTRING(email, 1, 5) takes the first five characters. Starting positions in SQL are 1-based across every major database, unlike most programming languages.

How do I concatenate strings in SQL?

The standard operator is ||, used by PostgreSQL, SQLite and Oracle. MySQL uses the CONCAT() function, and SQL Server uses + or CONCAT(). NULL handling differs: || returns NULL if any part is NULL in PostgreSQL and SQLite (Oracle treats the NULL as empty text), and so does + in SQL Server by default. CONCAT() skips NULLs in PostgreSQL, SQL Server and SQLite, but returns NULL in MySQL, where CONCAT_WS skips them.

Do string functions change the data stored in the table?

No. In a SELECT they only shape the output. To change stored values, use the same expression inside an UPDATE statement — for example SET email = LOWER(email).

How do I compare text case-insensitively in SQL?

The portable way is to apply LOWER (or UPPER) to both sides of the comparison. Whether plain = ignores case depends on the database and its collation: MySQL and SQL Server usually do, PostgreSQL and SQLite do not. In SQLite, LIKE ignores case for the letters A to Z while = does not.

About the author

SQL Practice

SQL Practice publishes free SQL tutorials, an in-browser playground and 42 practice exercises, built so you can run what you read. Every runnable example in our tutorials is executed against the site’s sample database before it is published. Read how we keep tutorials accurate, or report a mistake.