SQL String & Date Functions
String Functions
SELECT LENGTH('hello'); -- 5
SELECT UPPER('hello'), LOWER('HELLO');
SELECT TRIM(' hello '); -- 'hello'
SELECT SUBSTRING('hello world', 7, 5); -- 'world' (start, length)
SELECT LEFT('hello world', 5), RIGHT('hello world', 5);
SELECT POSITION('world' IN 'hello world'); -- 7
SELECT REPLACE('hello world', 'world', 'SQL');
SELECT CONCAT('hello', ' ', 'world');
SELECT 'hello' || ' ' || 'world'; -- PostgreSQL concat operator
SELECT CONCAT_WS(', ', 'Alice', 'Bob'); -- 'Alice, Bob' — separator-aware concat
SELECT LPAD('42', 5, '0'); -- '00042'
LIKE / ILIKE Pattern Matching
WHERE name LIKE 'A%' -- starts with A
WHERE name LIKE '%son' -- ends with son
WHERE name LIKE '_ohn' -- exactly 4 chars ending in "ohn"
WHERE name ILIKE 'alice%' -- case-insensitive (PostgreSQL)
WHERE name ~ '^[A-Z]' -- regex, case-sensitive (PostgreSQL)
% matches any sequence (including empty); _ matches exactly one character — the two most commonly confused wildcards in an interview.
Date & Time Functions
SELECT NOW(), CURRENT_DATE, CURRENT_TIME;
SELECT EXTRACT(YEAR FROM NOW()); -- 2026
SELECT EXTRACT(DOW FROM NOW()); -- 0=Sunday .. 6=Saturday
SELECT DATE_TRUNC('month', NOW()); -- 2026-07-01 00:00:00 — snaps down to the start of the unit
SELECT DATE_TRUNC('week', NOW()); -- Monday of the current week
SELECT NOW() + INTERVAL '7 days';
SELECT '2026-07-06'::DATE - '2026-01-01'::DATE; -- integer days between
SELECT AGE('2026-07-06', '1998-01-15'); -- '28 years 5 mons 22 days'
SELECT TO_CHAR(NOW(), 'YYYY-MM-DD HH24:MI:SS');
SELECT TO_DATE('06/07/2026', 'DD/MM/YYYY');
Common Date Filters — the Index-Friendly Rewrite
-- Works, but defeats an index on created_at (function wraps the column)
WHERE EXTRACT(YEAR FROM created_at) = 2026
-- Equivalent, index-friendly — see SQL Indexes & Query Performance
WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01'
WHERE created_at >= NOW() - INTERVAL '30 days' -- last 30 days
WHERE created_at::DATE = CURRENT_DATE - 1 -- yesterday
This is the same functional-index gotcha as [[SQL Indexes & Query Performance]] — wrapping an indexed date column in EXTRACT()/YEAR() forces evaluation per row; a range comparison lets the index do its job.
NULL-Handling Functions
SELECT COALESCE(phone, mobile, email, 'no contact') FROM users; -- first non-NULL
SELECT revenue / NULLIF(orders, 0) AS avg_order_value FROM summary; -- NULL instead of divide-by-zero error
SELECT NULLIF(TRIM(name), '') FROM users; -- blank string coerced to NULL
JSON Functions (PostgreSQL)
SELECT data->>'name' FROM users; -- extract as text
SELECT data->'address'->>'city' FROM users; -- nested access
SELECT * FROM products WHERE tags @> '["sale"]'; -- JSONB containment check
Interview Drills
- Why is
WHERE EXTRACT(YEAR FROM created_at) = 2026slower than a range comparison on a large indexed table? — Wrapping the indexed column in a function forces the database to evaluate that function on every row rather than using the index's stored raw values directly — rewriting ascreated_at >= '2026-01-01' AND created_at < '2027-01-01'lets a B-tree index oncreated_atbe used normally. - What's the difference between
NULLIF(x, 0)and just checkingx != 0before dividing? —NULLIF(orders, 0)returns NULL whenordersis 0, which makesrevenue / NULLIF(orders, 0)return NULL instead of raising a divide-by-zero error — it's a single-expression guard usable inline in a SELECT list, versus needing a CASE or application-level branch.
Cross-links
[[SQL Basics & Query Fundamentals]] · [[SQL Indexes & Query Performance]] · [[SQL Window Functions]]