1 增删改查核心语句
1.1 INSERT — 增
1.1.1 单行插入:最基础形式,明确指定列与值的对应关系。
INSERT INTO table_name (col1, col2, col3)
VALUES (val1, val2, val3);1.1.2 多行批量插入:一次提交多条记录,性能远优于循环单条插入。
INSERT INTO table_name (col1, col2)
VALUES (val1, val2),
(val3, val4),
(val5, val6);1.1.3 从查询结果插入:将 SELECT 的结果集直接写入目标表,ETL 场景核心手法。
INSERT INTO target_table (col1, col2)
SELECT col_a, col_b
FROM source_table
WHERE condition;1.1.4 冲突处理(Upsert):主键/唯一键冲突时自动转为更新,避免重复写入报错。
INSERT INTO table_name (id, col1, col2)
VALUES (1, 'a', 'b')
ON DUPLICATE KEY UPDATE
col1 = VALUES(col1),
col2 = VALUES(col2);1.2 DELETE — 删
1.2.1 条件删除:WHERE 是生命线,裸跑 DELETE 将清空全表。
DELETE FROM table_name
WHERE condition;1.2.2 多表关联删除:删除满足 JOIN 条件的 t1 行,t2 仅作筛选依据。
DELETE t1
FROM table1 t1
JOIN table2 t2 ON t1.fk = t2.id
WHERE t2.status = 'invalid';1.2.3 子查询删除:用子查询精确圈定待删集合。
DELETE FROM table_name
WHERE id IN (
SELECT id FROM other_table WHERE condition
);1.3 UPDATE — 改
1.3.1 单表条件更新:同样,WHERE 缺失即全表更新。
UPDATE table_name
SET col1 = val1,
col2 = val2
WHERE condition;1.3.2 关联更新(JOIN UPDATE):依据另一张表的数据更新当前表,数据同步场景高频使用。
UPDATE table1 t1
JOIN table2 t2 ON t1.fk = t2.id
SET t1.col1 = t2.col_a,
t1.col2 = t2.col_b
WHERE t2.status = 'active';1.3.3 条件表达式更新:CASE WHEN 实现一条语句多分支赋值,避免多次 UPDATE 往返。
UPDATE table_name
SET col1 = CASE
WHEN condition1 THEN val1
WHEN condition2 THEN val2
ELSE val3
END
WHERE id IN (1, 2, 3);1.4 SELECT — 查(大一统核心)
现实中 90% 的复杂查询都是对这一骨架的扩展与组合。
1.4.1 完整骨架:子句的书写顺序与执行顺序不同,执行顺序见注释。
SELECT col1, col2, AGG_FUNC(col3) -- ⑦ 投影
FROM table_name -- ① 确定数据源
JOIN other_table ON ... -- ② 关联
WHERE condition -- ③ 行级过滤(聚合前)
GROUP BY col1, col2 -- ④ 分组
HAVING AGG_FUNC(col3) > value -- ⑤ 组级过滤(聚合后)
ORDER BY col1 DESC -- ⑥ 排序
LIMIT N OFFSET M; -- ⑧ 分页1.4.2 JOIN 家族:所有多表关联的底层原语,现实中绝大多数用 INNER / LEFT。
-- 交集:两表都匹配才返回
INNER JOIN table2 ON t1.id = t2.fk
-- 左全保留:左表不匹配行以 NULL 填充右侧
LEFT JOIN table2 ON t1.id = t2.fk
-- 右全保留(可用 LEFT JOIN 反写替代,保持风格统一)
RIGHT JOIN table2 ON t1.id = t2.fk
-- 笛卡尔积(慎用)
CROSS JOIN table21.4.3 子查询三形态:
-- 标量子查询:返回单值,用于 SELECT / WHERE
SELECT name, (SELECT MAX(score) FROM scores WHERE uid = u.id) AS best
FROM users u;
-- IN 子查询:返回集合做过滤条件
SELECT * FROM orders
WHERE user_id IN (SELECT id FROM users WHERE city = 'Tokyo');
-- 派生表(内联视图):FROM 后的子查询,当作临时表使用
SELECT t.dept, t.avg_sal
FROM (
SELECT dept, AVG(salary) AS avg_sal
FROM employees
GROUP BY dept
) t
WHERE t.avg_sal > 50000;1.4.4 CTE(公用表表达式):用 WITH 给子查询命名,逻辑更清晰,可复用,支持递归。
WITH
active_users AS (
SELECT id, name FROM users WHERE status = 'active'
),
user_orders AS (
SELECT user_id, COUNT(*) AS cnt
FROM orders
GROUP BY user_id
)
SELECT u.name, o.cnt
FROM active_users u
JOIN user_orders o ON u.id = o.user_id
ORDER BY o.cnt DESC;1.4.5 窗口函数:在保留原始行的前提下叠加聚合/排名,是分析型查询的核武器。
SELECT
name,
dept,
salary,
RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rank_in_dept,
SUM(salary) OVER (PARTITION BY dept) AS dept_total,
LAG(salary,1) OVER (PARTITION BY dept ORDER BY salary DESC) AS prev_salary
FROM employees;2 执行顺序速查
理解执行顺序是写对 WHERE vs HAVING、列别名能否复用的关键。
FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
① ② ③ ④ ⑤ ⑥ ⑦ ⑧- 1 个陷阱:SELECT 里定义的别名,WHERE / HAVING 中不能直接用(执行时 SELECT 还没跑),ORDER BY 可以用。
- 1 个推论:窗口函数在 SELECT 阶段计算,因此也不能出现在 WHERE / GROUP BY 里,需套一层派生表或 CTE 才能过滤。
3 变种归一表
| 现实场景 | 底层语句组合 |
|---|---|
| 分页列表 | SELECT + ORDER BY + LIMIT/OFFSET |
| 去重统计 | SELECT DISTINCT / COUNT(DISTINCT) |
| 排行榜 | SELECT + RANK()/DENSE_RANK() 窗口函数 |
| 同比/环比 | SELECT + LAG()/LEAD() 窗口函数 |
| 树形递归 | WITH RECURSIVE CTE + SELECT |
| 数据同步 | INSERT … ON DUPLICATE KEY / UPDATE JOIN |
| 软删除 | UPDATE SET deleted_at = NOW() |
| 批量导入 | INSERT INTO … SELECT FROM |
| 多条件分流 | UPDATE / SELECT + CASE WHEN |