SQL string functions clean, combine and extract text, and they come up constantly in data validation: checking emails, normalising phone numbers, splitting names. Below are the functions themselves, then the classic string problems solved step by step.
String Functions
-- Length of string
SELECT name, LENGTH(name) AS name_length FROM employees;
-- UPPER and LOWER case
SELECT UPPER(name) AS name_upper, LOWER(email) AS email_lower FROM employees;
-- Substring: extract part of string
SELECT SUBSTRING(name, 1, 5) AS first_5_chars FROM employees;
SELECT SUBSTR(phone, 1, 3) AS area_code FROM employees; -- Oracle
-- TRIM: remove whitespace
SELECT TRIM(' Naveed ') AS clean_name; -- "Naveed"
SELECT LTRIM(' hello') AS left_trimmed; -- "hello"
SELECT RTRIM('hello ') AS right_trimmed; -- "hello"
-- REPLACE
SELECT REPLACE(phone, '-', '') AS clean_phone FROM employees;
-- CONCAT
SELECT CONCAT(first_name, ' ', last_name) AS full_name FROM employees;
SELECT first_name || ' ' || last_name AS full_name FROM employees; -- Oracle/PG
-- INSTR / POSITION: find position of substring
SELECT INSTR(email, '@') AS at_position FROM employees;
-- LEFT and RIGHT: extract from start or end
SELECT LEFT(name, 3) AS initials FROM employees;
SELECT RIGHT(phone, 4) AS last_4 FROM employees;
-- LPAD / RPAD: pad string to a length
SELECT LPAD(emp_id, 6, '0') AS padded_id FROM employees; -- 000042
-- CHAR_LENGTH vs LENGTH (important for Unicode)
-- LENGTH: bytes; CHAR_LENGTH: characters
SELECT CHAR_LENGTH('café') AS chars; -- 4 (MySQL)
String Aggregation: GROUP_CONCAT and STRING_AGG
-- GROUP_CONCAT (MySQL): combine names into one string
SELECT dept,
COUNT(*) AS members,
GROUP_CONCAT(name ORDER BY name SEPARATOR ', ') AS team
FROM employees GROUP BY dept;
-- Output: Engineering | 5 | Alice, Bob, Charlie, Naveed, Ravi
-- STRING_AGG (PostgreSQL / SQL Server)
SELECT dept, STRING_AGG(name, ', ' ORDER BY name) AS team
FROM employees GROUP BY dept;
-- Limit concat to top 3 names per dept (combine with RANK)
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
)
SELECT dept,
GROUP_CONCAT(name ORDER BY salary DESC SEPARATOR ' | ') AS top3_earners
FROM ranked WHERE rn <= 3 GROUP BY dept;
Common SQL String Problems
Product companies (Amazon, Flipkart, Google) ask string SQL problems to test:
1. Knowledge of REGEXP / LIKE patterns
2. String manipulation functions (SUBSTRING, INSTR, LOCATE, TRIM, REPLACE)
3. Data cleaning and validation logic
4. Ability to parse structured strings (emails, phone numbers, URLs)
Email Validation Pattern
-- Find users with INVALID email format
-- Valid: contains exactly one @, has domain with ., no spaces
-- MySQL: REGEXP (Regular Expression)
SELECT user_id, email FROM users
WHERE email NOT REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
-- Simpler version: check for @ and .domain
SELECT user_id, email FROM users
WHERE email NOT LIKE '%@%.%'
OR email LIKE '% %' -- contains space
OR email LIKE '@%' -- starts with @
OR INSTR(email, '@') = 0 -- no @ at all
OR LENGTH(email) - LENGTH(REPLACE(email, '@', '')) > 1; -- multiple @
-- PostgreSQL: use ~ for regex
SELECT user_id, email FROM users
WHERE email !~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
Extract Domain from Email
-- Extract domain part from email (everything after @)
SELECT email,
SUBSTRING(email, INSTR(email, '@') + 1) AS domain
FROM users;
-- naveed@gmail.com → gmail.com
-- Count users by email domain
SELECT
SUBSTRING(email, INSTR(email, '@') + 1) AS domain,
COUNT(*) AS user_count
FROM users
GROUP BY domain
ORDER BY user_count DESC;
-- Extract username (before @)
SELECT email,
LEFT(email, INSTR(email, '@') - 1) AS username
FROM users;
-- Extract top-level domain (.com, .in, .org)
SELECT email,
SUBSTRING_INDEX(email, '.', -1) AS tld
FROM users;
-- naveed@example.co.in → in
Phone Number Cleaning
-- Standardise phone numbers: remove spaces, dashes, brackets, +91
SELECT phone,
REGEXP_REPLACE(
REGEXP_REPLACE(phone, '^(\\+91|0091|91)', ''), -- remove country code
'[^0-9]', '' -- remove all non-digits
) AS clean_phone
FROM users;
-- MySQL without REGEXP_REPLACE (chained REPLACE)
SELECT
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(phone,
'+91',''), ' ',''), '-',''), '(',''), ')','') AS clean_phone
FROM users;
-- Validate: Indian mobile number (10 digits, starts with 6-9)
SELECT phone FROM users
WHERE phone REGEXP '^[6-9][0-9]{9}$';
-- Find duplicate phone numbers (after cleaning)
SELECT REGEXP_REPLACE(phone, '[^0-9]', '') AS clean_phone, COUNT(*) AS cnt
FROM users
GROUP BY clean_phone
HAVING COUNT(*) > 1;
Parse Structured Strings
-- Parse CSV-like data stored in a single column
-- product_tags column: "electronics,mobile,smartphone,samsung"
-- Extract first tag
SELECT product_id,
SUBSTRING_INDEX(product_tags, ',', 1) AS first_tag
FROM products;
-- Count number of tags (count commas + 1)
SELECT product_id,
LENGTH(product_tags) - LENGTH(REPLACE(product_tags, ',', '')) + 1 AS tag_count
FROM products;
-- Find products containing 'mobile' tag
SELECT * FROM products
WHERE FIND_IN_SET('mobile', product_tags) > 0;
-- FIND_IN_SET(value, csv_string) returns position or 0 if not found
-- Extract URL components
-- url: https://www.example.com/products/123?ref=home
SELECT url,
SUBSTRING_INDEX(SUBSTRING_INDEX(url, '://', -1), '/', 1) AS domain,
SUBSTRING_INDEX(SUBSTRING_INDEX(url, '/', -1), '?', 1) AS path_end
FROM pages;
Name Formatting Problems
-- Capitalise first letter of each word (Title Case)
-- No built-in INITCAP in MySQL (Oracle/PG have it)
-- MySQL approach for single-word names:
SELECT CONCAT(UPPER(LEFT(name,1)), LOWER(SUBSTRING(name,2))) AS formatted_name
FROM users;
-- Oracle / PostgreSQL:
SELECT INITCAP(name) AS formatted_name FROM users;
-- Split full name into first and last name
SELECT full_name,
SUBSTRING_INDEX(full_name, ' ', 1) AS first_name,
SUBSTRING_INDEX(full_name, ' ', -1) AS last_name
FROM users;
-- "Naveed Khan" → first_name="Naveed", last_name="Khan"
-- Find users whose first name and last name are the same
SELECT full_name FROM users
WHERE SUBSTRING_INDEX(full_name, ' ', 1) = SUBSTRING_INDEX(full_name, ' ', -1);
FAQs
How do you extract the domain from an email in SQL?
In MySQL use SUBSTRING_INDEX(email, '@', -1); in PostgreSQL SPLIT_PART(email, '@', 2).
What is the difference between CHAR_LENGTH and LENGTH?
In MySQL, CHAR_LENGTH counts characters and LENGTH counts bytes, so they differ for multi-byte UTF-8 text.
How do you concatenate values from several rows into one string?
Use GROUP_CONCAT in MySQL or STRING_AGG in PostgreSQL and SQL Server, together with GROUP BY.