Home Functions SUBSTRING()
SQL String Function

SUBSTRING()

SUBSTRING() extracts a portion of a string, starting at a given 1 based position and running for an optional number of characters.

MySQLPostgreSQLSQL ServerSQLite
Returns: A string containing the extracted portion. Returns an empty string when the requested range falls outside the input, and NULL when any argument is NULL.

Syntax

SUBSTRING(string, start)SUBSTRING(string, start, length)SUBSTRING(string FROM start FOR length)SUBSTRING(string FROM pattern)
ParameterTypeRequiredDescription
string string yes The source string to slice. Any expression that evaluates to text works, including a column.
start integer yes The 1 based position of the first character to return. Position 1 is the first character, not position 0.
length integer no How many characters to return. Omit it to read from start to the end of the string. SQL Server treats this as required.
pattern regular expression no PostgreSQL only. With SUBSTRING(string FROM pattern) it returns the first substring that matches the POSIX regular expression.

How it works

SUBSTRING() returns a slice of a string. You give it the source text, the position to start at, and optionally how many characters to take. It is the standard way to pull a fixed piece out of a larger value, such as the first three letters of a code or the year out of a formatted date.

The single most important rule is that SUBSTRING() is 1 based: position 1 is the first character, position 2 is the second, and so on. There is no position 0. If you omit the length, most engines return everything from the start position to the end of the string. SQL Server is the exception and expects all three arguments.

SUBSTR() is a common alias for the same operation in MySQL, Oracle and SQLite, so SUBSTR(name, 1, 3) and SUBSTRING(name, 1, 3) behave the same on those engines. PostgreSQL also accepts the SQL standard keyword form SUBSTRING(string FROM start FOR length), and a regex form SUBSTRING(string FROM pattern) that returns the first match of a pattern. To locate the start position dynamically, pair it with POSITION(), and use LEFT() or RIGHT() when you only need one end of the string.

Examples

Take the first 3 characters

-- Extract a country code prefix from a product SKU
SELECT sku,
       SUBSTRING(sku, 1, 3) AS prefix
FROM products;
Result
sku        | prefix
-----------+-------
 UAE100245 | UAE
 KSA773100 | KSA
 EGY000581 | EGY

Take a slice from the middle

-- Characters 4 through 6 (start at position 4, length 3)
SELECT SUBSTRING('UAE100245', 4, 3) AS middle;
Result
middle
------
100

From a position to the end (omit length)

-- Everything from position 4 onward
SELECT SUBSTRING('UAE100245', 4) AS tail;
Result
tail
------
100245

Using the SUBSTR alias

-- SUBSTR is identical to SUBSTRING on MySQL, Oracle and SQLite
SELECT SUBSTR(email, 1, 5) AS start_of_email
FROM customers;
Result
start_of_email
--------------
 omara
 laila
 hasan

PostgreSQL keyword form

-- SQL standard FROM ... FOR ... syntax
SELECT SUBSTRING('2026-07-05' FROM 1 FOR 4) AS year;
Result
year
----
2026

PostgreSQL regex form

-- Return the first run of digits in the string
SELECT SUBSTRING('order#4821 paid' FROM '[0-9]+') AS order_no;
Result
order_no
--------
4821

Common mistakes

Wrong
-- Assuming positions start at 0 like an array
-- This skips the first character you wanted
SELECT SUBSTRING('hello', 0, 3);  -- 'he', not 'hel'
Right
-- Positions are 1 based: 1 is the first character
SELECT SUBSTRING('hello', 1, 3);  -- 'hel'

SUBSTRING() is 1 based, not 0 based. Position 1 is the first character. Passing 0 (or a negative start) makes some engines count from before the string, so the first character you expected gets consumed by the offset.

Wrong
-- Expecting a negative start to work the same everywhere
-- On SQL Server this does NOT count from the end
SELECT SUBSTRING('hello', -3, 3);  -- '' on SQL Server
Right
-- MySQL and SQLite count from the end with a negative start
SELECT SUBSTRING('hello', -3);      -- 'llo' on MySQL
-- On SQL Server, use LEN() to compute the position instead
SELECT SUBSTRING('hello', LEN('hello') - 2, 3);  -- 'llo'

Negative start behaviour differs by engine. MySQL and SQLite treat a negative start as an offset from the end of the string, while SQL Server and PostgreSQL treat positions below 1 as starting before the string, which shortens the result. Do not assume a negative start is portable.

Wrong
-- SQL Server requires the length argument
SELECT SUBSTRING('hello', 2) AS rest;  -- syntax error
Right
-- Provide a length, or use a large number to reach the end
SELECT SUBSTRING('hello', 2, LEN('hello')) AS rest;  -- 'ello'

Omitting length works on MySQL, PostgreSQL and SQLite but not on SQL Server before 2022, where the three argument form is required. When you want the rest of the string, pass a length large enough to reach the end, such as LEN(column).

Performance

SUBSTRING() itself is cheap: it is a straightforward character copy over one value. The performance cost shows up when you wrap a column in it inside a WHERE clause, because WHERE SUBSTRING(code, 1, 3) = 'UAE' is not sargable and forces a full scan. The database cannot use a normal index on code when the column is buried inside a function.

When you filter on a fixed prefix, prefer a range or LIKE predicate such as code LIKE 'UAE%', which can use an index on code. If you must filter on a computed slice, create a functional or computed column index over the same SUBSTRING() expression so the optimiser can seek it directly.

For large text values, remember that SUBSTRING() counts characters, not bytes, on most engines, so multibyte encodings do the right thing but add a small amount of work. See the indexing guide for why wrapping columns in functions defeats index seeks.

Interview questions

Is SUBSTRING() 0 based or 1 based?

It is 1 based. Position 1 is the first character of the string, position 2 is the second, and there is no position 0. This trips up developers used to 0 based array indexing in most programming languages.

What is the difference between SUBSTRING() and SUBSTR()?

They do the same thing. SUBSTR() is a common alias for SUBSTRING() in MySQL, Oracle and SQLite. SQL Server uses only SUBSTRING(), and PostgreSQL supports both plus the SQL standard SUBSTRING(string FROM start FOR length) keyword form.

SELECT SUBSTR('hello', 1, 3);      -- MySQL, Oracle, SQLite
SELECT SUBSTRING('hello', 1, 3);   -- all four engines

How do you extract everything from a position to the end of a string?

Omit the length argument: SUBSTRING(col, 4) returns from position 4 to the end on MySQL, PostgreSQL and SQLite. On SQL Server the length is required, so pass a length large enough to reach the end, such as LEN(col).

SELECT SUBSTRING('UAE100245', 4);           -- '100245'
SELECT SUBSTRING('UAE100245', 4, LEN('UAE100245'));  -- SQL Server

How do you pull a variable length piece out of a string when you do not know the position in advance?

Combine SUBSTRING() with a search function. Use POSITION() (or CHARINDEX on SQL Server) to find where the delimiter is, then feed that into the start argument of SUBSTRING(). For example, extract the local part of an email before the @ sign.

SELECT SUBSTRING(email, 1, POSITION('@' IN email) - 1) AS local_part
FROM customers;

What happens if the start position is beyond the length of the string?

You get an empty string, not NULL and not an error. The requested range simply falls outside the input, so there is nothing to return. If any argument itself is NULL, the whole result is NULL.

Master SQL, one function at a time

Browse the full SQL functions library, or learn the fundamentals with our free, structured courses.