这是一份面向日常开发的 MySQL 8.x SQL 速查手册。示例统一使用 users、orders 等表,覆盖查询、写入、聚合、关联、事务、索引与排查。
执行 UPDATE、DELETE、ALTER TABLE 或 DROP 前,请先确认环境、备份策略和影响行数。应用代码应使用参数化查询,避免拼接 SQL。
一、基础查询 SELECT
查询全部或指定列
SELECT * FROM users;
SELECT id, name, age
FROM users;别名与计算列
SELECT
id,
name AS user_name,
price * quantity AS amount
FROM order_items;二、条件过滤
WHERE、AND 与 OR
SELECT id, name
FROM users
WHERE age >= 18
AND status = 1
AND (city = 'SH' OR city = 'BJ');LIKE、IN、BETWEEN 与 NULL
SELECT * FROM users WHERE name LIKE '%Tom%';
SELECT * FROM users WHERE city IN ('BJ', 'SH');
SELECT * FROM users WHERE age BETWEEN 18 AND 30;
SELECT * FROM users WHERE deleted_at IS NULL;NULL 不能使用 = NULL 或 != NULL 判断,应使用 IS NULL 或 IS NOT NULL。LIKE 以 % 开头时通常难以利用普通 B-Tree 索引。
三、排序、去重与分页
SELECT DISTINCT city
FROM users
ORDER BY city ASC;
SELECT id, name, created_at
FROM users
ORDER BY created_at DESC, id DESC
LIMIT 10 OFFSET 20;LIMIT 的偏移量从 0 开始。深分页时 OFFSET 越大,扫描和丢弃的数据通常越多;高数据量接口可考虑基于稳定索引列进行游标分页。
SELECT id, name, created_at
FROM users
WHERE id < :last_id
ORDER BY id DESC
LIMIT 20;四、聚合、分组与 HAVING
SELECT
city,
COUNT(*) AS user_count,
AVG(age) AS avg_age,
MAX(age) AS max_age,
MIN(age) AS min_age
FROM users
WHERE status = 1
GROUP BY city
HAVING COUNT(*) > 10
ORDER BY user_count DESC;- WHERE 在分组前过滤明细行。
- HAVING 在分组后过滤聚合结果。
- 开启 ONLY_FULL_GROUP_BY 时,非聚合列应满足分组规则。
五、多表 JOIN
INNER JOIN:只保留匹配行
SELECT u.id, u.name, o.order_no
FROM users AS u
INNER JOIN orders AS o ON o.user_id = u.id;LEFT JOIN:保留左表全部行
SELECT u.id, u.name, COUNT(o.id) AS order_count
FROM users AS u
LEFT JOIN orders AS o ON o.user_id = u.id
GROUP BY u.id, u.name;查找没有订单的用户
SELECT u.id, u.name
FROM users AS u
LEFT JOIN orders AS o ON o.user_id = u.id
WHERE o.id IS NULL;关联字段通常应建立合适索引。把右表过滤条件放在 WHERE 还是 ON 中,可能改变 LEFT JOIN 的结果语义。
六、子查询与 CTE
查询订单数最多的用户
SELECT u.*
FROM users AS u
WHERE u.id = (
SELECT o.user_id
FROM orders AS o
GROUP BY o.user_id
ORDER BY COUNT(*) DESC
LIMIT 1
);使用 CTE 拆分复杂查询(MySQL 8+)
WITH order_stats AS (
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
)
SELECT u.id, u.name, s.order_count
FROM users AS u
JOIN order_stats AS s ON s.user_id = u.id
WHERE s.order_count >= 10;七、新增、修改与删除
INSERT
INSERT INTO users (name, age, email)
VALUES ('Tom', 20, 'tom@example.com');
INSERT INTO users (name, age, email)
VALUES
('Tom', 20, 'tom@example.com'),
('Lucy', 22, 'lucy@example.com');UPDATE
UPDATE users
SET age = 25,
updated_at = CURRENT_TIMESTAMP
WHERE id = 1;DELETE
DELETE FROM users
WHERE id = 1;写操作前可以先用相同 WHERE 执行 SELECT,确认目标行。生产环境中应限制账号权限,并关注受影响行数。
八、事务与行锁
START TRANSACTION;
SELECT balance
FROM accounts
WHERE id = 1
FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- 出错时执行:ROLLBACK;- 事务中的表应使用支持事务的存储引擎,例如 InnoDB。
- 保持事务短小,避免在持锁期间执行网络请求或等待用户输入。
- 按固定顺序访问资源,有助于降低死锁概率。
九、创建与修改表
常用建表模板
CREATE TABLE users (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
age INT UNSIGNED DEFAULT NULL,
email VARCHAR(100) NOT NULL,
status TINYINT NOT NULL DEFAULT 1,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_users_email (email),
KEY idx_users_status_created (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;ALTER TABLE
ALTER TABLE users ADD COLUMN avatar VARCHAR(255) NULL;
ALTER TABLE users MODIFY COLUMN age INT UNSIGNED DEFAULT NULL;
ALTER TABLE users DROP COLUMN avatar;DDL 可能锁表、重建表或消耗大量 I/O。对大表执行前,应结合 MySQL 版本、存储引擎和 Online DDL 支持情况评估。
十、索引与执行计划
创建、查看与删除索引
CREATE INDEX idx_users_age ON users (age);
CREATE UNIQUE INDEX uk_users_email ON users (email);
SHOW INDEX FROM users;
DROP INDEX idx_users_age ON users;查看执行计划
EXPLAIN
SELECT id, name
FROM users
WHERE status = 1
ORDER BY created_at DESC
LIMIT 20;
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 1001;- 重点关注访问类型、候选索引、实际使用索引、预估行数与额外操作。
- 联合索引的列顺序应结合常用过滤、排序和分组条件设计。
- 索引会增加写入与存储成本,不是越多越好。
十一、日期与常用函数
SELECT NOW(), CURRENT_DATE, CURRENT_TIMESTAMP;
SELECT *
FROM orders
WHERE created_at >= '2026-01-01 00:00:00'
AND created_at < '2026-02-01 00:00:00';
SELECT CONCAT(name, ' <', email, '>') AS display_name
FROM users;
SELECT COALESCE(nickname, name) AS display_name
FROM users;按时间范围查询时,推荐使用左闭右开区间,避免月底天数和微秒精度问题。尽量不要对索引列包裹函数,否则可能影响索引使用。
十二、开发安全清单
- 使用预编译参数,绝不直接拼接用户输入。
- 业务账号遵循最小权限原则,避免使用 root 连接应用。
- UPDATE 与 DELETE 必须确认 WHERE 条件和影响行数。
- 分页查询必须使用稳定排序,避免结果漂移。
- 上线索引或 DDL 前先在接近生产数据量的环境验证。
- 慢查询先看 EXPLAIN,再结合慢日志和实际负载判断。