MySQL Cheat Sheet

mysql is the command-line client that ships with MySQL. This sheet lists the MySQL commands and query syntax you reach for most, from logging in to mysqldump, with a worked example under every table.

Updated Oct 12, 2026Tested against MySQL 8.4.11 LTS

Also works with TiDB.

Connecting and Login

Default port is 3306. Build a URL or DSN for your host with the MySQL connection string generator.

CommandWhat it does
Log in to localhost, prompt for the password
Log in and select the shop database
Connect to a remote host and port
Force TCP instead of the local socket
Connect through a specific Unix socket
TLS, checking the certificate and host name
Save credentials in an obfuscated file
Log in with saved credentials
Deprecated; the password shows in the process environment
Run one statement and exit
Who you logged in as and the current database
Connection status: server version, user, TLS, charset
Leave the client (or , or Ctrl+D)

On Amazon RDS with IAM auth, a rejected token shows up as Access denied for user on RDS MySQL.

Example: save a login path once, then connect to production over TLS without typing the password again.

bash
mysql_config_editor set --login-path=prod --host=db.example.com --user=app --password
mysql --login-path=prod --ssl-mode=VERIFY_IDENTITY --ssl-ca=rds-ca.pem shop
mysql_config_editor print --all   # passwords are shown as *****

Databases

CommandWhat it does
List every database you can see
List databases matching a pattern
Create a database with the server defaults
Create with an explicit charset and collation
Switch the current database
Show the current database
Charset, collation and encryption of a database
Change the default charset for new tables
DestructiveDelete the database and every table in it
DestructiveSame, without an error when it is missing

There is no . Create the new database and move each table with .

Example: rename the shop database to store by moving every table across, then drop the empty one.

sql
CREATE DATABASE store CHARACTER SET utf8mb4;
RENAME TABLE shop.users TO store.users, shop.orders TO store.orders;
SHOW TABLES FROM shop;   -- empty set
DROP DATABASE shop;

Tables

CommandWhat it does
List tables in the current database
List tables in another database by pattern
List only views
Columns, types, nulls, keys and defaults
Same, plus collation and comments
The full CREATE TABLE statement, one field per line
Engine, row estimate, data and index size
Empty copy with the same columns and indexes
Add a column at a set position
Change a column's type or nullability
Rename a column, keeping its definition
DestructiveRemove a column and its data
Add a foreign key
Rename a table
DestructiveDelete every row and reset AUTO_INCREMENT
DestructiveDelete the table and its data

Example: the two tables every other example on this page uses.

sql
CREATE TABLE users (
  id         BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  email      VARCHAR(255) NOT NULL UNIQUE,
  name       VARCHAR(100) NOT NULL,
  plan       ENUM('free', 'pro', 'team') NOT NULL DEFAULT 'free',
  credit     DECIMAL(10, 2) NOT NULL DEFAULT 0,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
  id         BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  user_id    BIGINT UNSIGNED NOT NULL,
  total      DECIMAL(10, 2) NOT NULL,
  status     VARCHAR(20) NOT NULL DEFAULT 'pending',
  meta       JSON NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users (id)
);

Users and Privileges

An account is a user name plus a host, so and are two different accounts.

CommandWhat it does
List users and their auth plugin
Create a user that can log in from any host
Allow logins only from a subnet
Full rights on one database
Read-only on one database
Rights on one table
What a user is allowed to do
Your own privileges
Take rights away
Change a password
Block logins without dropping the user
Rename an account
DestructiveDelete an account
Reload grant tables; only needed after editing them directly

Example: a read-only reporting user limited to the office network, checked before you hand it out.

sql
CREATE USER 'report'@'10.0.%' IDENTIFIED BY 'Rep0rt!only';
GRANT SELECT ON shop.* TO 'report'@'10.0.%';
SHOW GRANTS FOR 'report'@'10.0.%';
-- GRANT USAGE ON *.* TO `report`@`10.0.%`
-- GRANT SELECT ON `shop`.* TO `report`@`10.0.%`

Insert, Update, Delete

CommandWhat it does
Insert one row
Insert several rows in one statement
Insert the result of a query
First AUTO_INCREMENT id from your last insert
Skip rows that hit a unique key, as warnings
Upsert: insert, or update the existing row
DestructiveDeletes the old row, then inserts
Update matching rows
Update with a date cutoff
Update with a join
Delete matching rows
Delete in batches to keep locks short
Delete with a join

The function in still works but is deprecated. Use a row alias () after , or a derived table after .

Example: record a user's daily order count with one upsert, safe to run again for the same day.

sql
CREATE TABLE daily_orders (
  user_id BIGINT UNSIGNED, day DATE, orders INT, PRIMARY KEY (user_id, day)
);
INSERT INTO daily_orders (user_id, day, orders)
SELECT * FROM (
  SELECT user_id, DATE(created_at) AS day, COUNT(*) AS n FROM orders
  WHERE created_at >= CURDATE() GROUP BY user_id, DATE(created_at)
) AS t
ON DUPLICATE KEY UPDATE orders = t.n;

Select and Filtering

CommandWhat it does
Filter rows by value
Match any value in a list
Pattern match; any run, one character
Exclude a pattern
Regular expression match
Inclusive range on both ends
NULL check; never matches
Not equal ( also works)
Sort by several columns
Page 3 of 20 rows
Unique values
Count and sum per group
Filter groups after aggregating
Add a grand total row
Conditional column
Rows that have a match in another table

Example: the top five customers by October spend, using a CTE ().

sql
WITH october AS (
  SELECT user_id, SUM(total) AS spent, COUNT(*) AS orders
  FROM orders
  WHERE status = 'paid' AND created_at >= '2026-10-01' AND created_at < '2026-11-01'
  GROUP BY user_id
)
SELECT u.email, o.spent, o.orders
FROM october o JOIN users u ON u.id = o.user_id
ORDER BY o.spent DESC
LIMIT 5;

Joins and Unions

CommandWhat it does
Inner join: rows with a match on both sides
Every user, orders or NULL
Users with no orders
Every user; same as the LEFT JOIN flipped
Join on a column both tables share
Every combination of rows
Combine results, removing duplicates
Combine and keep duplicates; faster
Rows in both results (8.0.31+)
Rows in the first result only (8.0.31+)

MySQL has no . Combine a LEFT and a RIGHT join with .

Example: a full outer join between users and leads on email.

sql
SELECT u.email, l.source FROM users u LEFT JOIN leads l ON l.email = u.email
UNION
SELECT l.email, l.source FROM users u RIGHT JOIN leads l ON l.email = u.email;

Common Functions

CommandWhat it does
Current datetime, date and UTC datetime
Format a date; is minutes, is month
Days between two dates (30)
Difference in any unit
Date arithmetic
First non-NULL value
Replace NULL with a default
Inline if/else
Join strings; NULL if any part is NULL
Part of a string, 1-based
Everything after the last @
Join a group's values into one string
Round to 2 decimal places

Example: a monthly revenue report with formatted months and a comma-separated list of statuses.

sql
SELECT DATE_FORMAT(created_at, '%Y-%m') AS month,
       ROUND(SUM(total), 2) AS revenue,
       GROUP_CONCAT(DISTINCT status ORDER BY status SEPARATOR ', ') AS statuses
FROM orders
GROUP BY month
ORDER BY month DESC;

Import, Export and Backup

CommandWhat it does
Back up one database without locking InnoDB tables
Include procedures, triggers and events
Back up several databases, with CREATE DATABASE
Back up the whole server
Schema only, no rows
One table, filtered rows
Compressed backup
Import a .sql file into a database
Import a compressed dump
Run a .sql file from inside the client
Write a CSV on the server host
The only folder INTO OUTFILE and LOAD DATA may use
Load a CSV from your machine

needs on the server and on the client.

In 1bench: the MySQL GUI exports a table, with your current filter and sort, to CSV, JSON, NDJSON or SQL on your own machine, so there is no INTO OUTFILE path or secure_file_priv folder to fight, and it imports CSV, JSON and SQL files the same way.

Example: copy a production database to staging in one pipe, without writing a dump file.

bash
mysqldump --login-path=prod --single-transaction --routines --triggers shop \
  | mysql --login-path=staging shop_staging

CLI Output and Options

CommandWhat it does
Print each column on its own line
Tab-separated output, no borders
Same, without the header row
Table borders even when output is piped
HTML table output ( for XML)
Rough CSV; breaks on values with commas or tabs
Page wide results with horizontal scroll
Same, from inside the client; turns it off
Copy everything you run and see to a file
Echo each statement before its result
Refuse UPDATE and DELETE without a key in WHERE
Warnings from the last statement
Built-in syntax help for any statement

Your typed history is in . Lines that look like or are left out of it.

Example: export a query to CSV from the shell, with a header row, for a spreadsheet.

bash
mysql --login-path=prod -B -e "
  SELECT id, user_id, total, status, created_at FROM shop.orders
  WHERE created_at >= '2026-10-01'" | tr '\t' ',' > orders-october.csv
head -2 orders-october.csv   # id,user_id,total,status,created_at

Indexes and EXPLAIN

CommandWhat it does
Every index, column order and cardinality
Add a composite index
Add a unique index
Add an index, fail rather than block writes
Full-text index for MATCH ... AGAINST
Hide an index from the optimizer to test dropping it
Remove an index
The plan: index used, rows estimated
The plan as an indented tree
Run the query and show real times and row counts
Make the optimizer use an index
Refresh index statistics

In EXPLAIN output, means a full table scan and means no index was used.

Example: check that a user's order history uses the composite index, then confirm with real timings.

sql
CREATE INDEX idx_orders_user ON orders (user_id, created_at);
EXPLAIN SELECT id, total FROM orders WHERE user_id = 7 ORDER BY created_at DESC LIMIT 10;
-- type: ref, key: idx_orders_user, Extra: Backward index scan
EXPLAIN ANALYZE SELECT id, total FROM orders WHERE user_id = 7 ORDER BY created_at DESC LIMIT 10;

Processes and Transactions

CommandWhat it does
Running connections, truncated statements
Same, full statement text
Busy connections, longest first
Stop the running statement, keep the connection
DestructiveClose the connection and roll back its transaction
Your own connection id
Begin a transaction ( also works)
Save the transaction
Undo everything since START TRANSACTION
Mark a point to roll back to
Undo only back to the savepoint
Lock the row until commit
Change isolation for this session
Last deadlock, lock waits, buffer pool stats
Current connection count
Connection limit before "Too many connections"

In 1bench: the MySQL GUI cancels a running query from the tab, sending KILL QUERY on a side connection, so you do not need to look up the thread id first.

Example: move credit between two users atomically, locking both rows first.

sql
START TRANSACTION;
SELECT id, credit FROM users WHERE id IN (1, 2) FOR UPDATE;
UPDATE users SET credit = credit - 25.00 WHERE id = 1;
UPDATE users SET credit = credit + 25.00 WHERE id = 2;
COMMIT;

JSON

CommandWhat it does
Read a JSON value, quoted ()
Read it unquoted ()
Long form of , array index from 0
Filter on a JSON field
Array contains a value
Set or add a key
Remove a key
Build a JSON object from columns
Aggregate rows into a JSON array
Index a JSON field through a generated column

Example: turn each order's items array into rows with to count units sold per SKU.

sql
SELECT jt.sku, SUM(jt.qty) AS units
FROM orders,
     JSON_TABLE(meta, '$.items[*]' COLUMNS (sku VARCHAR(40) PATH '$.sku', qty INT PATH '$.qty')) AS jt
GROUP BY jt.sku
ORDER BY units DESC;

TiDB Notes

TiDB speaks the MySQL protocol on port 4000, so the client and the SQL above work. These rows are TiDB only.

CommandWhat it does
Connect to a local TiDB (default port 4000)
TiDB version and build details
Read the table as it was 5 minutes ago
Random ids that spread writes across nodes
Delete rows automatically after 90 days
Bulk load CSV files fast
Optimizer hint to pick an index
Allow one query up to 8 GiB of memory
Slowest recent statements
Check SQL mode, which TiDB keeps MySQL compatible

Example: after a bad delete, read the rows as they were before it ran. Stale reads only reach back as far as the GC window (, 10 minutes by default).

sql
SELECT COUNT(*) FROM orders WHERE user_id = 7;   -- 0 after the bad delete
SELECT * FROM orders AS OF TIMESTAMP NOW() - INTERVAL 5 MINUTE
WHERE user_id = 7;                               -- the rows as they were

Gotchas

  • or without a clause changes every row, and autocommit is on by default, so there is no undo. Start the client with , or run first and check the row count before .
  • is not an upsert. It deletes the old row and inserts a new one, which fires delete triggers, cascades foreign keys and drops any column you left out back to its default. Use .
  • without locks tables while it runs. With it, InnoDB gets a consistent snapshot, but MyISAM tables are still not consistent.
  • in MySQL is an alias for , which cannot store emoji or other 4-byte characters. Use .
  • (no space) puts the password in shell history and process lists, and (with a space) treats Secret as the database name. Use alone or a login path.
  • on a connection running a large write rolls back everything it did, which can take as long as the write itself. Use when you only want the statement stopped.

Work with MySQL without memorizing it

1bench is a desktop GUI for MySQL. Connect, browse your data and run queries in one window, with the CLI still there when you want it.

Open MySQL in 1bench

MySQL FAQ

How do I check the MySQL version?
From the shell, mysql --version prints the client version and mysqld --version prints the server binary's version. Once connected, SELECT VERSION(); returns the version of the server you are actually talking to, which is the one that matters when the client and server came from different installs. The status command (\s) in the client shows it too, along with the connection and character sets.
Is MySQL case sensitive?
Keywords and column names are not. String comparisons follow the column's collation, and the 8.4 default, utf8mb4_0900_ai_ci, ignores case and accents, so WHERE email = '[email protected]' matches [email protected]. Use a _bin or _as_cs collation, or COLLATE utf8mb4_bin in the query, for a case-sensitive match. Table and database names follow lower_case_table_names, which is 0 (case sensitive) on Linux and 2 on macOS.
Does MySQL have a boolean data type?
BOOL and BOOLEAN are accepted but stored as TINYINT(1). TRUE and FALSE are just 1 and 0, so a BOOLEAN column will also accept 5 or -1 unless you add a CHECK (flag IN (0, 1)) constraint. SHOW CREATE TABLE prints the column as tinyint(1), which is how drivers and ORMs know to map it back to a boolean.
Where does MySQL store its data and error log?
Run SELECT @@datadir, @@log_error; to get the exact paths for the running server. Each InnoDB table usually lives in its own .ibd file under a folder named after the database. Common defaults are /var/lib/mysql on Linux packages, /usr/local/mysql/data for the macOS installer and /opt/homebrew/var/mysql for Homebrew on Apple silicon. Copy the folder only with the server stopped; use mysqldump for a live backup.
How do I reset the MySQL root password?
Stop the server, then start it with --skip-grant-tables --skip-networking so it lets anyone in from the local socket only. Connect with mysql -u root, run FLUSH PRIVILEGES; to load the grant tables again, then ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password';. Stop the server and start it normally. Skipping FLUSH PRIVILEGES makes the ALTER USER fail, because grant tables are off.