UPPER() and LOWER()
UPPER() converts a string to all uppercase; LOWER() converts it to all lowercase. Both are supported identically in MySQL, PostgreSQL, and SQLite.
SELECT UPPER('hello world'); -- 'HELLO WORLD'
SELECT LOWER('SQL Is FUN'); -- 'sql is fun'
-- Practical: normalise email comparison
SELECT *
FROM users
WHERE LOWER(email) = LOWER('User@Example.COM');
-- Format display names
SELECT
UPPER(LEFT(first_name, 1)) || LOWER(SUBSTRING(first_name, 2)) AS formatted_name
FROM employees;
Wrap both sides in LOWER() for case-insensitive comparisons instead of relying on database collation settings β this works consistently across all databases.
LENGTH() and CHAR_LENGTH()
LENGTH() returns the number of characters (or bytes, depending on dialect). CHAR_LENGTH() always returns the character count regardless of encoding β prefer it when working with multi-byte Unicode characters.
SELECT LENGTH('Hello'); -- 5
SELECT CHAR_LENGTH('Hello'); -- 5
-- Find products with names longer than 30 characters
SELECT product_name, CHAR_LENGTH(product_name) AS name_len
FROM products
WHERE CHAR_LENGTH(product_name) > 30
ORDER BY name_len DESC;
-- Validate phone number format (must be exactly 10 digits)
SELECT phone
FROM contacts
WHERE LENGTH(REPLACE(phone, '-', '')) != 10;
SUBSTRING()
SUBSTRING(str, start, length) extracts a portion of a string. Note: SQL string positions start at 1, not 0.
SELECT SUBSTRING('Hello World', 1, 5); -- 'Hello'
SELECT SUBSTRING('Hello World', 7); -- 'World' (no length = rest of string)
SELECT SUBSTR('Hello World', 7, 5); -- 'World' (SUBSTR is an alias)
-- Extract year from an ISO date string
SELECT order_id, SUBSTRING(order_date, 1, 4) AS order_year
FROM orders;
-- Get domain from email
SELECT
email,
SUBSTRING(email, POSITION('@' IN email) + 1) AS domain
FROM users;
LEFT(str, n) extracts the first n characters; RIGHT(str, n) extracts the last n. They are available in MySQL and PostgreSQL as convenient alternatives to SUBSTRING.
SELECT LEFT('Hello World', 5); -- 'Hello'
SELECT RIGHT('Hello World', 5); -- 'World'
-- Extract country code from phone
SELECT RIGHT(phone, 4) AS last_four
FROM customers;
CONCAT()
CONCAT combines two or more strings into one. MySQL uses the CONCAT() function; PostgreSQL and SQLite support the || operator (which is also valid in MySQL 8+).
-- MySQL / PostgreSQL / SQLite function style
SELECT CONCAT('Hello', ' ', 'World'); -- 'Hello World'
-- PostgreSQL / SQLite operator style
SELECT 'Hello' || ' ' || 'World'; -- 'Hello World'
-- Build full name from separate columns
SELECT
CONCAT(first_name, ' ', last_name) AS full_name,
CONCAT(city, ', ', country) AS location
FROM employees;
-- MySQL CONCAT_WS: join with separator (skips NULLs automatically)
SELECT CONCAT_WS(', ', street, city, state, zip) AS address
FROM addresses;
TRIM, LTRIM, RTRIM
Trim functions remove leading and/or trailing characters (whitespace by default) from a string. Essential for cleaning user-entered data.
SELECT TRIM(' hello '); -- 'hello'
SELECT LTRIM(' hello '); -- 'hello ' (left only)
SELECT RTRIM(' hello '); -- ' hello' (right only)
-- Remove a specific character (PostgreSQL / MySQL)
SELECT TRIM(LEADING '0' FROM '0001234'); -- '1234'
SELECT TRIM(TRAILING '.' FROM 'price.'); -- 'price'
SELECT TRIM(BOTH '*' FROM '***sale***'); -- 'sale'
-- Practical: clean up imported data
UPDATE products
SET product_name = TRIM(product_name)
WHERE product_name != TRIM(product_name);
REPLACE()
REPLACE(str, find, replacement) replaces every occurrence of find in str with replacement. The replacement can be an empty string to delete characters.
SELECT REPLACE('Hello World', 'World', 'SQL'); -- 'Hello SQL'
SELECT REPLACE('2026-06-08', '-', '/'); -- '2026/06/08'
-- Remove all spaces
SELECT REPLACE('hello world', ' ', ''); -- 'helloworld'
-- Standardise phone numbers: remove dashes and spaces
SELECT REPLACE(REPLACE(phone, '-', ''), ' ', '') AS clean_phone
FROM contacts;
-- Mask sensitive data (keep last 4 digits)
SELECT
CONCAT(REPEAT('*', LENGTH(card_number) - 4),
RIGHT(card_number, 4)) AS masked_card
FROM payments;
POSITION() / INSTR() / STRPOS()
These functions find the starting position of a substring within a string. They return 0 (MySQL) or 0/NULL depending on dialect when not found.
-- ANSI SQL (MySQL, PostgreSQL)
SELECT POSITION('@' IN 'user@example.com'); -- 5
-- MySQL / SQLite alternative
SELECT INSTR('user@example.com', '@'); -- 5
-- PostgreSQL alternative
SELECT STRPOS('user@example.com', '@'); -- 5
-- Find emails that contain a sub-domain
SELECT email
FROM users
WHERE POSITION('.' IN SUBSTRING(email, POSITION('@' IN email))) > 0;
-- Return 0 if not found
SELECT POSITION('xyz' IN 'Hello World'); -- 0 (not found)
Dialect Comparison Table
| Function | MySQL | PostgreSQL | SQLite |
|---|---|---|---|
| Uppercase | UPPER() | UPPER() | UPPER() |
| Lowercase | LOWER() | LOWER() | LOWER() |
| String length | CHAR_LENGTH() | LENGTH() | LENGTH() |
| Extract substring | SUBSTRING() | SUBSTRING() | SUBSTR() |
| Concatenate | CONCAT() | || or CONCAT() | || |
| Trim whitespace | TRIM() | TRIM() | TRIM() |
| Find position | INSTR() | STRPOS() | INSTR() |
| First N chars | LEFT() | LEFT() | SUBSTR(s,1,n) |
| Last N chars | RIGHT() | RIGHT() | SUBSTR(s,-n) |
| Repeat string | REPEAT() | REPEAT() | No built-in |
Summary
π Summary
- UPPER() / LOWER() β Convert case. Use for case-insensitive comparisons.
- LENGTH() / CHAR_LENGTH() β Count characters. Prefer CHAR_LENGTH() for Unicode safety.
- SUBSTRING(str, start, len) β Extract a portion. SQL positions start at 1.
- CONCAT() / || β Join strings. Use CONCAT_WS() to join with separator and skip NULLs.
- TRIM / LTRIM / RTRIM β Remove leading/trailing characters. Great for data cleaning.
- REPLACE(str, find, new) β Replace all occurrences. Pass empty string to delete.
- POSITION / INSTR / STRPOS β Find substring position. Returns 0 if not found.
- LEFT(str, n) / RIGHT(str, n) β Shorthand for extracting from start or end.
FAQ
In MySQL, LENGTH() returns the byte length of the string, while CHAR_LENGTH() returns the character count. For ASCII text they are the same. For multi-byte characters (e.g. Chinese, Arabic, emoji), LENGTH() may return a larger number. In PostgreSQL and SQLite, LENGTH() always returns character count. Use CHAR_LENGTH() in MySQL to be safe with Unicode data.
In MySQL, CONCAT() returns NULL if any argument is NULL. To handle NULLs gracefully, use CONCAT_WS(separator, ...) which skips NULL arguments, or wrap each argument in COALESCE(col, ''). In PostgreSQL, the || operator also returns NULL if either operand is NULL, so use COALESCE() around nullable columns.
No β SQL keywords and built-in function names are case-insensitive. UPPER(), upper(), and Upper() all work. By convention, function names are written in uppercase to distinguish them from column or table names, but the database treats them the same way.
SQL uses 1-based string indexing by the ANSI standard β the first character is at position 1. This differs from most programming languages (Python, JavaScript) which use 0-based indexing. If you pass 0 as the start position in MySQL it is treated as 1; in PostgreSQL it is treated as returning from the start of the string.