Home Blog SQL Tips

The SQL Cheat Sheet: All Commands

SQL Tips Mohammad July 5, 2026

This is the SQL command reference you keep open in a second tab. Every command that matters - grouped by what it does, each with real syntax and a short runnable example - so you can find the exact shape you need in seconds, then adapt and move on.

SQL is a big language, but you use a small, repeating core of it every day. The trick is knowing which command belongs to which job: DDL defines structure, DML changes data, DQL reads it, and a handful of clauses (JOIN, GROUP BY, WHERE, window functions) shape the results. This page walks the whole surface area in that order. Where the big three databases disagree - MySQL, PostgreSQL, and SQL Server - the differences are called out inline so you are not caught out when you switch engines.

How to use this cheat sheet

Read it once top to bottom to see the shape of the language, then treat it as a lookup. Every command sits under the category that matches its job, so if you want to read data you jump to the SELECT section, and if you want to change structure you jump to DDL. Each entry gives you the syntax skeleton plus a tiny, runnable example you can paste into your client and adapt by swapping table and column names. The examples all use one small schema - a customers table and an orders table - so the pieces connect as you scroll. Where a keyword differs between engines, a blue note tells you the MySQL, PostgreSQL, and SQL Server spelling so you never copy syntax that silently fails on the database in front of you. Nothing here needs to be memorised: the goal is to make the right shape findable in seconds.

1. DDL - defining structure

Data Definition Language commands create and change the shape of the database itself: databases, tables, indexes, and views. They are not undone by a rollback in most engines (they auto-commit), so treat them with care. For a deeper walkthrough see the DDL commands guide.

CommandPurposeMinimal example
CREATE DATABASEMake a new databaseCREATE DATABASE shop;
CREATE TABLEDefine a table and its columnsCREATE TABLE t (id INT);
CREATE INDEXSpeed up lookups on a columnCREATE INDEX ix ON t(id);
CREATE VIEWSave a query as a virtual tableCREATE VIEW v AS SELECT ...;
ALTER TABLEAdd, change, or drop columnsALTER TABLE t ADD c INT;
DROPDelete an object entirelyDROP TABLE t;
TRUNCATEEmpty a table fast, keep structureTRUNCATE TABLE t;
RENAMERename a tableRENAME TABLE t TO t2;

CREATE

Start with a database, then the tables inside it. IF NOT EXISTS makes the statement safe to re-run.

SQLCREATE DATABASE IF NOT EXISTS shop; CREATE TABLE customers ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, email VARCHAR(150) UNIQUE, country CHAR(2), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

Auto-increment differs by engine. MySQL uses AUTO_INCREMENT, PostgreSQL uses SERIAL or GENERATED ALWAYS AS IDENTITY, and SQL Server uses IDENTITY(1,1). The column concept is the same; only the keyword changes.

An index makes reads on a column fast; a view stores a query under a name so you can select from it like a table.

SQL-- Index the column you filter or join on most CREATE INDEX idx_customers_country ON customers(country); -- A view is a saved query, queried like a table CREATE VIEW uae_customers AS SELECT id, name, email FROM customers WHERE country = 'AE';

ALTER TABLE

Change a table after it exists. The three moves you make constantly are add, modify, and drop a column.

SQL-- Add a column ALTER TABLE customers ADD phone VARCHAR(20); -- Change a column's type (MySQL) ALTER TABLE customers MODIFY phone VARCHAR(30); -- Drop a column ALTER TABLE customers DROP COLUMN phone;

MODIFY vs ALTER COLUMN. MySQL changes a column type with MODIFY column type. PostgreSQL and SQL Server use ALTER COLUMN - Postgres as ALTER COLUMN phone TYPE VARCHAR(30), SQL Server as ALTER COLUMN phone VARCHAR(30). Same intent, three spellings.

DROP, TRUNCATE & RENAME

DROP removes the object and all its data. TRUNCATE keeps the empty table but wipes every row instantly and resets identity counters - it is far faster than DELETE for clearing a whole table but cannot be filtered with WHERE. RENAME just changes a name.

SQLDROP TABLE IF EXISTS old_logs; TRUNCATE TABLE staging_orders; RENAME TABLE customers TO clients; -- MySQL -- PostgreSQL / SQL Server: ALTER TABLE customers RENAME TO clients;

TRUNCATE vs DELETE. Both empty rows, but they are not the same. DELETE is DML: it removes rows one at a time, can be filtered with WHERE, fires row triggers, and can be rolled back. TRUNCATE is DDL: it deallocates the whole table's data in one operation, ignores WHERE, resets the identity counter, and in MySQL auto-commits so it cannot be undone. Reach for TRUNCATE only when you truly want the table completely empty and fast.

2. Data types

Pick the smallest type that comfortably holds your data. The exact names vary a little between engines, but they group into four families.

FamilyCommon typesUse for
NumericINT, BIGINT, SMALLINT, DECIMAL(p,s), NUMERIC, FLOAT, DOUBLEIDs, counts, money (use DECIMAL, never FLOAT, for currency)
StringCHAR(n), VARCHAR(n), TEXTNames, emails, notes; CHAR for fixed-length codes
Date / timeDATE, TIME, DATETIME, TIMESTAMPCalendar dates, timestamps, audit columns
Boolean / otherBOOLEAN, ENUM, JSON, UUID, BLOBFlags, fixed choice lists, structured data, binaries

Booleans are not universal. PostgreSQL has a real BOOLEAN. MySQL treats BOOLEAN as an alias for TINYINT(1). SQL Server has no boolean type - use BIT (0 or 1). Store money as DECIMAL(10,2) everywhere to avoid rounding errors.

3. Constraints

Constraints are rules the database enforces on every row so bad data never lands. Declare them in CREATE TABLE or add them later with ALTER TABLE.

ConstraintGuaranteesExample
PRIMARY KEYUnique + not null; the row's identityid INT PRIMARY KEY
FOREIGN KEYValue must exist in another tableREFERENCES customers(id)
UNIQUENo two rows share this valueemail VARCHAR(150) UNIQUE
NOT NULLA value is always requiredname VARCHAR(100) NOT NULL
CHECKValue passes a conditionCHECK (total >= 0)
DEFAULTFills a value when none is givenstatus VARCHAR(20) DEFAULT 'new'
AUTO_INCREMENTAuto-numbers new rowsid INT AUTO_INCREMENT
SQLCREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, customer_id INT NOT NULL, total DECIMAL(10,2) CHECK (total >= 0), status VARCHAR(20) DEFAULT 'new', FOREIGN KEY (customer_id) REFERENCES customers(id) );

4. DML - changing data

Data Manipulation Language adds, edits, and removes rows. The full tour lives in the DML commands guide; here is the fast version.

INSERT

SQL-- Single row INSERT INTO customers (name, email, country) VALUES ('Sara Ahmed', 'sara@example.com', 'AE'); -- Multiple rows in one statement INSERT INTO customers (name, country) VALUES ('Omar Ali', 'SA'), ('Lina Yusuf', 'EG'); -- Insert the result of a query INSERT INTO archive_customers (id, name) SELECT id, name FROM customers WHERE created_at < '2024-01-01';

UPDATE and DELETE

Both take a WHERE clause that decides which rows are affected. Leave it off and you hit every row in the table.

SQLUPDATE customers SET country = 'SA' WHERE id = 42; DELETE FROM customers WHERE id = 42;

Always test the WHERE first. Run SELECT * FROM customers WHERE id = 42; before the UPDATE or DELETE. If the SELECT returns the exact rows you mean to change, the write is safe.

UPSERT - insert or update

An upsert inserts a row, or updates it if a key already exists. Each engine spells it differently.

MySQLINSERT INTO stock (sku, qty) VALUES ('A1', 5) ON DUPLICATE KEY UPDATE qty = qty + 5;
PostgreSQLINSERT INTO stock (sku, qty) VALUES ('A1', 5) ON CONFLICT (sku) DO UPDATE SET qty = stock.qty + 5;
SQL ServerMERGE stock AS t USING (SELECT 'A1' AS sku, 5 AS qty) AS s ON t.sku = s.sku WHEN MATCHED THEN UPDATE SET t.qty = t.qty + s.qty WHEN NOT MATCHED THEN INSERT (sku, qty) VALUES (s.sku, s.qty);

5. DQL - the SELECT statement

SELECT is the command you write most. The clauses always appear in this order, though the database evaluates them roughly as FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT. See the DQL commands guide for the full clause order.

SQLSELECT DISTINCT country, COUNT(*) AS customers FROM customers WHERE created_at >= '2025-01-01' GROUP BY country HAVING COUNT(*) > 10 ORDER BY customers DESC LIMIT 5;
  • Columns: list them explicitly instead of SELECT * in real code - it is faster and self-documenting.
  • DISTINCT: removes duplicate rows from the result.
  • Aliases: AS renames a column or table (SUM(total) AS revenue, customers AS c).
  • ORDER BY: sort with ASC (default) or DESC; sort by several columns for tie-breaks.
  • LIMIT / OFFSET: paginate results.

Here is the same set of clauses at a glance - what each one does and the smallest example that shows it working.

ClauseJobTiny example
WHEREKeep only rows that match a conditionWHERE total > 100
DISTINCTDrop duplicate rowsSELECT DISTINCT country
GROUP BYFold rows into one per groupGROUP BY country
HAVINGFilter groups after aggregationHAVING COUNT(*) > 10
ORDER BYSort the result setORDER BY total DESC
LIMITCap how many rows come backLIMIT 5

Row limiting is engine-specific. MySQL and PostgreSQL use LIMIT 10 OFFSET 20. SQL Server uses OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY (or the older SELECT TOP 10). Same goal, different syntax.

6. Operators & wildcards

The WHERE clause is where the logic lives. These operators cover nearly every real filter.

GroupOperatorsExample
Comparison= <> < > <= >=total >= 100
LogicalAND OR NOTcountry = 'AE' AND total > 0
RangeBETWEEN a AND btotal BETWEEN 100 AND 500
ListIN (...)status IN ('paid','shipped')
PatternLIKE with % and _email LIKE '%@gmail.com'
Null testIS NULL / IS NOT NULLshipped_at IS NULL

In LIKE patterns, % matches any run of characters (including none) and _ matches exactly one character. So 'S%' is anything starting with S, and 'S_ _ _' (no spaces) matches a four-letter name beginning with S.

SQLSELECT name FROM customers WHERE name LIKE 'S%' -- starts with S AND email LIKE '%@gmail.com' -- ends with gmail AND country IN ('AE', 'SA') AND phone IS NOT NULL;

NULL is not equal to anything - not even another NULL. Test missing values with IS NULL / IS NOT NULL, never = NULL. Note also that PostgreSQL LIKE is case-sensitive (use ILIKE for case-insensitive), while MySQL LIKE is usually case-insensitive by default.

7. JOINs

A JOIN combines rows from two tables on a matching key. The type controls what happens to rows that have no match on the other side. Here is one-line syntax for each.

JoinReturnsSyntax
INNEROnly rows matched in both tablesFROM a INNER JOIN b ON a.id = b.a_id
LEFTAll left rows; NULLs where right has no matchFROM a LEFT JOIN b ON a.id = b.a_id
RIGHTAll right rows; NULLs where left has no matchFROM a RIGHT JOIN b ON a.id = b.a_id
FULLAll rows from both sidesFROM a FULL JOIN b ON a.id = b.a_id
CROSSEvery combination (Cartesian product)FROM a CROSS JOIN b
SELFA table joined to itselfFROM emp e JOIN emp m ON e.mgr_id = m.id
SQL-- Every order with its customer's name SELECT o.id, c.name, o.total FROM orders o INNER JOIN customers c ON c.id = o.customer_id; -- All customers, even those with zero orders SELECT c.name, COUNT(o.id) AS orders FROM customers c LEFT JOIN orders o ON o.customer_id = c.id GROUP BY c.name;

MySQL has no FULL JOIN. Emulate it by combining a LEFT JOIN and a RIGHT JOIN with UNION. PostgreSQL and SQL Server support FULL OUTER JOIN directly. Want the full picture? Read the deep dive on JOINs.

8. Set operators

Set operators stack the results of two SELECTs that have the same columns. UNION removes duplicates; UNION ALL keeps them and is faster. INTERSECT returns rows in both; EXCEPT returns rows in the first but not the second.

SQLSELECT email FROM customers UNION SELECT email FROM newsletter_signups; -- Emails on both lists SELECT email FROM customers INTERSECT SELECT email FROM newsletter_signups;

MySQL added INTERSECT and EXCEPT in 8.0.31. Older MySQL versions have only UNION / UNION ALL; emulate the rest with joins or NOT IN. SQL Server calls the difference operator EXCEPT; Oracle calls it MINUS.

9. Aggregate, string, date & numeric functions

Aggregate functions collapse many rows into one value; scalar functions transform a single value. The full catalogue is in the SQL functions reference and the functions list.

SQLSELECT COUNT(*) AS orders, SUM(total) AS revenue, AVG(total) AS avg_order, MIN(total) AS smallest, MAX(total) AS largest FROM orders;

String functions

FunctionDoesExample → result
CONCATJoin stringsCONCAT(name,' (',country,')')
UPPER / LOWERChange caseUPPER('ae')AE
LENGTHCharacter countLENGTH('sql')3
SUBSTRINGSlice textSUBSTRING('sql',1,2)sq
TRIMStrip surrounding spacesTRIM(' hi ')hi
REPLACESwap substringsREPLACE('a-b','-','_')a_b
LEFT / RIGHTFirst / last N charactersLEFT('sql-cheat',3)sql
POSITION / LOCATEIndex of a substringPOSITION('@' IN email)
LPAD / RPADPad to a fixed widthLPAD('7',3,'0')007

Date & numeric functions

FunctionDoesExample
NOW / CURRENT_TIMESTAMPCurrent date and timeSELECT NOW();
CURRENT_DATEToday's dateWHERE due = CURRENT_DATE
DATEDIFFDays between datesDATEDIFF(d2, d1)
EXTRACTPull a part of a dateEXTRACT(YEAR FROM created_at)
ROUNDRound a numberROUND(19.99, 1)20.0
COALESCEFirst non-null valueCOALESCE(phone, 'n/a')
DATE_ADD / + INTERVALShift a date by an intervalDATE_ADD(due, INTERVAL 7 DAY)
CEIL / FLOORRound up / down to integerCEIL(19.1)20
ABSAbsolute valueABS(-42)42
MOD / %Remainder of a divisionMOD(10, 3)1
CASTConvert between typesCAST('42' AS INT)

String concatenation varies wildly. Standard SQL and PostgreSQL use the || operator (name || ' ' || country). MySQL uses CONCAT() (its || means OR by default). SQL Server uses + or CONCAT(). When in doubt, CONCAT() works on MySQL, Postgres, and SQL Server alike.

10. Subqueries & CTEs

A subquery is a SELECT nested inside another statement. A CTE (Common Table Expression) with WITH does the same job but reads top to bottom, which is far easier to follow when queries get long. Both appear in the advanced SQL queries guide.

SubquerySELECT name FROM customers WHERE id IN (SELECT customer_id FROM orders WHERE total > 500);
CTEWITH big_spenders AS ( SELECT customer_id, SUM(total) AS spent FROM orders GROUP BY customer_id HAVING SUM(total) > 1000 ) SELECT c.name, b.spent FROM big_spenders b JOIN customers c ON c.id = b.customer_id;

A recursive CTE references itself to walk hierarchies - org charts, category trees, number series. Use WITH RECURSIVE.

Recursive CTEWITH RECURSIVE nums AS ( SELECT 1 AS n -- anchor row UNION ALL SELECT n + 1 FROM nums -- recursive step WHERE n < 10 ) SELECT n FROM nums; -- 1..10

SQL Server drops the RECURSIVE keyword. It writes recursive CTEs with plain WITH nums AS (...); MySQL and PostgreSQL require WITH RECURSIVE. The anchor-plus-UNION ALL structure is identical.

11. Window functions

Window functions compute across a set of rows related to the current row - running totals, rankings, row numbers - without collapsing them the way GROUP BY does. Every row stays; you just get an extra calculated column. The OVER clause defines the window, optionally partitioned and ordered.

FunctionReturns
ROW_NUMBER()Sequential number per row in the window
RANK()Rank with gaps after ties (1,1,3)
DENSE_RANK()Rank with no gaps (1,1,2)
LAG() / LEAD()Value from the previous / next row
SUM() OVERRunning or partitioned total
SQL-- Rank orders within each country by size SELECT country, id, total, ROW_NUMBER() OVER (PARTITION BY country ORDER BY total DESC) AS rn, LAG(total) OVER (PARTITION BY country ORDER BY id) AS prev_total FROM orders;

For the full treatment - frames, RANGE vs ROWS, and real reporting patterns - see the window functions guide.

12. Views, indexes & transactions

Views

A view is a stored query you can select from like a table. It simplifies complex logic and hides columns you do not want exposed.

SQLCREATE VIEW monthly_revenue AS SELECT DATE_FORMAT(created_at, '%Y-%m') AS month, SUM(total) AS revenue FROM orders GROUP BY month; SELECT * FROM monthly_revenue; -- query it like a table

Indexes

An index is a lookup structure that turns a full table scan into a fast seek. Index the columns you filter, join, and sort on - but not every column, since each index slows down writes.

SQLCREATE INDEX idx_orders_customer ON orders(customer_id); CREATE UNIQUE INDEX idx_customers_email ON customers(email); DROP INDEX idx_orders_customer ON orders; -- MySQL syntax

Transactions

A transaction groups statements so they all succeed or all roll back together - essential for money moves and any multi-step write. SAVEPOINT lets you roll back part of the way.

SQLBEGIN; -- or START TRANSACTION UPDATE accounts SET balance = balance - 100 WHERE id = 1; SAVEPOINT after_debit; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT; -- or ROLLBACK to undo everything

DDL usually cannot be rolled back. In MySQL, statements like CREATE, ALTER, and DROP auto-commit and silently end any open transaction. PostgreSQL is the exception - most of its DDL is fully transactional and can sit inside BEGIN ... ROLLBACK.

13. How often you will use each command

Not all commands earn equal time. In everyday application and analytics work, reading and filtering data dominates; schema changes are occasional. This is a representative picture of where your keystrokes actually go.

Share of everyday SQL you will write
SELECT / WHERE92%
JOIN74%
GROUP BY / aggregates61%
INSERT / UPDATE / DELETE55%
CTEs / subqueries40%
Window functions28%
DDL (CREATE / ALTER)18%

The takeaway: master the SELECT family and JOINs first, because they are 80 percent of the job. Everything else in this sheet is the long tail you reach for when a specific problem demands it. Bookmark this page, then go deeper with the focused guides - the SQL syntax guide for exact grammar, the functions list for every built-in, and the beginner course to build it all into muscle memory.

Turn this cheat sheet into fluency

A reference gets you unstuck; practice makes it stick. Work through structured, runnable lessons that take you from your first SELECT to advanced queries.