The SQL syntax you actually use — selects, filters, joins, aggregates, window functions and data changes — each with a copy-paste example. Need a query this page doesn't cover? SQLGenie writes it from plain English for free, no account needed.
SELECT basics
Everything starts with SELECT: pick columns (or * for all), name the table, and limit what comes back.
-- All columns, all rowsSELECT * FROM customers;
-- Specific columns, renamed for readabilitySELECT first_name AS name,
email,
created_at AS signup_date
FROM customers;
-- No duplicate valuesSELECTDISTINCT country FROM customers;
-- Just the first 10 rowsSELECT * FROM orders LIMIT10;
Filtering with WHERE
WHERE keeps only the rows you want. Combine conditions with AND/OR, and use IN, BETWEEN and LIKE for sets, ranges and patterns.
SELECT * FROM orders
WHERE total > 100AND status = 'paid';
SELECT * FROM products
WHERE category IN ('audio', 'video');
SELECT * FROM events
WHERE event_date BETWEEN'2026-01-01'AND'2026-12-31';
-- % matches any characters; _ matches exactly oneSELECT * FROM users
WHERE email LIKE'%@gmail.com';
-- NULL needs IS, not =SELECT * FROM employees
WHERE manager_id ISNULL;
Sorting and limiting
ORDER BY sorts the result; pair it with LIMIT (or FETCH FIRST on SQL Server) to get top-N queries.
-- Newest orders firstSELECT * FROM orders
ORDER BY created_at DESC;
-- Sort by two columnsSELECT * FROM products
ORDER BY category ASC, price DESC;
-- Top 5 best sellersSELECT product_id, COUNT(*) AS times_sold
FROM order_items
GROUP BY product_id
ORDER BY times_sold DESCLIMIT5;
Joins
INNER JOIN keeps matching rows only; LEFT JOIN keeps every left row; FULL OUTER JOIN keeps both sides. Always join on a key.
-- Matching rows onlySELECT c.name, o.total
FROM customers c
INNER JOIN orders o ON o.customer_id = c.id;
-- Every customer, orders if they existSELECT c.name, o.total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;
-- Customers with NO ordersSELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.id ISNULL;
-- Join three tablesSELECT c.name, p.title, oi.quantity
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
JOIN customers c ON c.id = o.customer_id
JOIN products p ON p.id = oi.product_id;
Aggregates and GROUP BY
COUNT, SUM, AVG, MIN and MAX summarize rows. GROUP BY summarizes per group; HAVING filters the groups afterwards.
SELECTCOUNT(*) AS total_orders,
SUM(total) AS revenue,
AVG(total) AS average_order
FROM orders;
-- Revenue per customerSELECT customer_id, SUM(total) AS revenue
FROM orders
GROUP BY customer_id;
-- Only big spenders (HAVING, not WHERE)SELECT customer_id, SUM(total) AS revenue
FROM orders
GROUP BY customer_id
HAVINGSUM(total) > 1000;
CASE expressions
CASE is SQL's if-then-else — label rows, build custom sort orders, or count conditionally.
-- Label rows by valueSELECT id, total,
CASEWHEN total >= 500THEN'large'WHEN total >= 100THEN'medium'ELSE'small'ENDAS order_size
FROM orders;
-- Count conditionallySELECTCOUNT(CASEWHEN status = 'paid'THEN1END) AS paid_orders
FROM orders;
Window functions
Window functions compute across related rows without collapsing them — running totals, ranks and row-over-row comparisons.
-- Rank employees by salary within each departmentSELECT name, department, salary,
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_rank
FROM employees;
-- Running total of revenueSELECT order_date,
SUM(total) OVER (ORDER BY order_date) AS running_total
FROM orders;
-- Compare with the previous rowSELECT sale_date, amount,
amount - LAG(amount) OVER (ORDER BY sale_date) AS change
FROM sales;
CTEs and subqueries
A common table expression (WITH) names a query so you can build on it — much easier to read than nested subqueries.
WITH big_orders AS (
SELECT customer_id, SUM(total) AS revenue
FROM orders
GROUP BY customer_id
HAVINGSUM(total) > 1000
)
SELECT c.name, b.revenue
FROM big_orders b
JOIN customers c ON c.id = b.customer_id
ORDER BY b.revenue DESC;
-- Same idea as a subquerySELECT name
FROM customers
WHERE id IN (
SELECT customer_id FROM orders WHERE total > 500
);
Insert, update and delete
Writing data: INSERT adds rows, UPDATE changes them, DELETE removes them. Always test the WHERE with a SELECT first.
INSERTINTO customers (name, email, country)
VALUES ('Ada Lovelace', 'ada@example.com', 'UK');
UPDATE products
SET price = price * 1.05WHERE category = 'audio';
-- Preview first: run the SELECT, then the DELETESELECT * FROM sessions WHERE expires_at < NOW();
DELETEFROM sessions WHERE expires_at < NOW();
Handy string and date functions
The functions you reach for daily. Syntax varies slightly between MySQL, PostgreSQL and SQL Server.
-- Combine and clean textSELECT UPPER(first_name) || ' ' || UPPER(last_name) AS shouty_name
FROM customers;
SELECT TRIM(email) FROM users;
-- Extract date partsSELECT EXTRACT(YEAR FROM created_at) AS signup_year
FROM users;
-- Replace NULL with a fallbackSELECT name, COALESCE(phone, 'no phone on file') AS phone
FROM customers;
-- Length and substringSELECT LENGTH(description), SUBSTRING(description FROM1 FOR 50)
FROM products;
Frequently asked questions
What is the fastest way to learn SQL syntax?
Learn the five building blocks first — SELECT, WHERE, JOIN, GROUP BY and ORDER BY — because most real queries are combinations of those. Then practice on questions you actually have about real data: turning a question into a query (or using a text-to-SQL tool like SQLGenie and reading the result) sticks far better than memorizing syntax tables.
Does this cheat sheet work for MySQL, PostgreSQL and SQL Server?
The core syntax — SELECT, WHERE, JOIN, GROUP BY, HAVING, ORDER BY — is standard SQL and works the same everywhere. The differences are in the details: limiting rows (LIMIT vs TOP vs FETCH FIRST), string concatenation, and date functions. SQLGenie generates dialect-correct SQL for your specific database if you pick it from the dropdown.
What is the difference between WHERE and HAVING?
WHERE filters individual rows before grouping and cannot use aggregates like SUM or COUNT. HAVING filters groups after GROUP BY and can. Execution order: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY.
What is the difference between INNER JOIN and LEFT JOIN?
INNER JOIN returns only rows that match in both tables. LEFT JOIN returns every row from the left table, filling the right side with NULL when there is no match — so it is the right choice when missing matches should still appear.
Need a query that's not on this sheet?
Describe what you want in plain English, paste your table structure, and SQLGenie writes the SQL for your exact database — with an explanation so you learn the syntax as you go.