SQL 语法速查
MySQL SQL 语法交互式速查表
本章是一份快速参考指南,涵盖 MySQL 最常用的 SQL 语句和函数。每个概念都配有代码示例和实际应用场景。无论你是初学者还是有经验的开发者,都可以快速查找和学习 SQL 语法。
**使用建议:**本章采用"示例驱动"的方式,每个 SQL 语句都配有具体的使用场景。建议在本地 MySQL 环境复制示例并运行,以加深理解。
SELECT — 查询数据
基础 SELECT 语句
最简单的 SELECT
-- 查询表中所有列和所有行
SELECT * FROM employees;
-- 查询指定列
SELECT employee_id, name, salary FROM employees;
-- 查询指定数量的行
SELECT * FROM employees LIMIT 10;
-- 从第 10 行开始查询 5 行(MySQL 8.0+)
SELECT * FROM employees LIMIT 10, 5;
-- 或使用 OFFSET(更清晰)
SELECT * FROM employees LIMIT 5 OFFSET 10;
列别名和表达式
-- 使用别名
SELECT
employee_id,
name,
salary * 12 AS annual_salary,
salary * 1.1 AS next_year_salary
FROM employees;
-- 使用 CASE 表达式
SELECT
name,
salary,
CASE
WHEN salary >= 5000 THEN 'Senior'
WHEN salary >= 3000 THEN 'Mid-level'
ELSE 'Junior'
END AS level
FROM employees;
DISTINCT — 去重
-- 查询所有不同的部门
SELECT DISTINCT department FROM employees;
-- 按多列去重
SELECT DISTINCT department, job_title FROM employees;
WHERE — 条件筛选
| 操作符 | 含义 | 示例 |
|---|---|---|
| = | 等于 | salary = 5000 |
| !=, <> | 不等于 | department != 'Sales' |
| <, >, <=, >= | 比较 | salary > 3000 |
| IN | 在列表中 | department IN ('IT', 'HR') |
| NOT IN | 不在列表中 | id NOT IN (1, 2, 3) |
| BETWEEN | 范围内(包含边界) | salary BETWEEN 3000 AND 5000 |
| LIKE | 字符串匹配(% 任意字符,_ 单个字符) | name LIKE 'John%' |
| IS NULL | 为空 | phone IS NULL |
| IS NOT NULL | 非空 | email IS NOT NULL |
| AND | 逻辑与 | salary > 3000 AND department = 'IT' |
| OR | 逻辑或 | status = 'active' OR status = 'pending' |
WHERE 常见模式
-- 组合条件
SELECT * FROM employees
WHERE (department = 'IT' OR department = 'HR')
AND salary >= 4000;
-- BETWEEN 用于范围查询(更高效)
SELECT * FROM orders
WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31';
-- LIKE 用于模糊查询(注意性能)
SELECT * FROM users
WHERE email LIKE '%@gmail.com';
-- IN 用于多值匹配
SELECT * FROM products
WHERE category_id IN (1, 3, 5, 7);
JOIN — 表联接
四种 JOIN 类型
INNER JOIN — 内连接(只保留匹配的行)
-- 查询每个员工及其部门信息
SELECT
e.employee_id,
e.name,
d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;
LEFT JOIN — 左外连接(保留左表所有行)
-- 查询所有员工及其部门(即使没有部门也保留)
SELECT
e.employee_id,
e.name,
COALESCE(d.department_name, 'No Department') AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;
RIGHT JOIN — 右外连接(保留右表所有行)
-- 查询所有部门及其员工(即使部门没有员工也保留)
SELECT
d.department_name,
COUNT(e.employee_id) AS employee_count
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id
GROUP BY d.department_id;
FULL OUTER JOIN — 全外连接(MySQL 不原生支持,使用 UNION)
-- MySQL 模拟 FULL OUTER JOIN
SELECT
COALESCE(e.employee_id, -1) AS emp_id,
COALESCE(e.name, 'No Employee') AS employee_name,
COALESCE(d.department_name, 'No Department') AS department
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
UNION
SELECT
COALESCE(e.employee_id, -1),
COALESCE(e.name, 'No Employee'),
COALESCE(d.department_name, 'No Department')
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id
WHERE e.employee_id IS NULL;
多表 JOIN
-- 联接三个表:员工 → 部门 → 部门经理
SELECT
e.employee_id,
e.name,
d.department_name,
m.name AS manager_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
INNER JOIN employees m ON d.manager_id = m.employee_id;
性能提示: - 总是在 JOIN 条件中使用索引列(通常是主键和外键) - 先用 WHERE 过滤数据,再 JOIN(减少联接的行数) - 避免在 JOIN 条件中使用函数(如 YEAR(date_column) = 2024) - 通过 EXPLAIN 检查 JOIN 顺序和是否使用了索引
子查询 — 嵌套查询
标量子查询(返回单个值)
-- 找出高于平均薪资的员工
SELECT
name,
salary
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);
列子查询(返回一列)
-- 查询属于某些部门的员工
SELECT * FROM employees
WHERE department_id IN (
SELECT department_id FROM departments
WHERE location = 'New York'
);
关联子查询(子查询引用外层表)
-- 查询每个部门最高薪资的员工
SELECT
e.employee_id,
e.name,
e.salary
FROM employees e
WHERE e.salary = (
SELECT MAX(salary)
FROM employees
WHERE department_id = e.department_id
);
**性能警告:**关联子查询会对外层每一行执行一次子查询,导致大量数据库访问。用 JOIN 或窗口函数替代会快很多。
CTE — 通用表表达式
基础 CTE(WITH 子句)
-- 定义一个临时结果集,然后在主查询中使用
WITH high_earners AS (
SELECT employee_id, name, salary
FROM employees
WHERE salary > 5000
)
SELECT
department_id,
COUNT(*) AS count,
AVG(salary) AS avg_salary
FROM high_earners
GROUP BY department_id;
递归 CTE(MySQL 8.0+)
-- 生成从 1 到 10 的数字列表(示例)
WITH RECURSIVE numbers AS (
-- 基础查询(递归的终止点)
SELECT 1 AS n
UNION ALL
-- 递归查询
SELECT n + 1 FROM numbers WHERE n < 10
)
SELECT * FROM numbers;
组织结构树查询
-- 查询员工的完整管理链(从员工到顶级经理)
WITH RECURSIVE emp_hierarchy AS (
-- 基础:选择指定员工
SELECT
employee_id, name, manager_id, 0 AS level
FROM employees
WHERE employee_id = 101
UNION ALL
-- 递归:逐级向上查询经理
SELECT
e.employee_id, e.name, e.manager_id, eh.level + 1
FROM employees e
INNER JOIN emp_hierarchy eh ON e.employee_id = eh.manager_id
)
SELECT * FROM emp_hierarchy;
聚合函数 — 分组统计
常用聚合函数
| 函数 | 含义 | 示例 |
|---|---|---|
| COUNT() | 计数 | COUNT(*) / COUNT(DISTINCT id) |
| SUM() | 求和 | SUM(salary) |
| AVG() | 平均值 | AVG(salary) |
| MIN() | 最小值 | MIN(hire_date) |
| MAX() | 最大值 | MAX(salary) |
| GROUP_CONCAT() | 字符串拼接 | GROUP_CONCAT(name SEPARATOR ', ') |
GROUP BY — 分组聚合
-- 按部门统计员工数和平均薪资
SELECT
department_id,
COUNT(*) AS emp_count,
AVG(salary) AS avg_salary,
MIN(salary) AS min_salary,
MAX(salary) AS max_salary
FROM employees
GROUP BY department_id;
HAVING — 分组后过滤
-- 查询平均薪资超过 4000 的部门
SELECT
department_id,
AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 4000;
DML — 数据修改
INSERT — 插入数据
-- 插入单行
INSERT INTO employees (name, salary, department_id)
VALUES ('Alice', 5000, 1);
-- 插入多行
INSERT INTO employees (name, salary, department_id) VALUES
('Bob', 4500, 2),
('Charlie', 5500, 1),
('Diana', 4000, 3);
-- 从另一个表插入数据
INSERT INTO employees_backup (name, salary, department_id)
SELECT name, salary, department_id
FROM employees
WHERE hire_date > '2024-01-01';
UPDATE — 更新数据
-- 更新单列
UPDATE employees SET salary = 5500 WHERE employee_id = 1;
-- 更新多列
UPDATE employees
SET
salary = salary * 1.1,
updated_at = NOW()
WHERE department_id = 1;
-- 批量更新(使用 JOIN)
UPDATE employees e
INNER JOIN departments d ON e.department_id = d.department_id
SET e.salary = e.salary * 1.05
WHERE d.location = 'New York';
DELETE — 删除数据
-- 删除符合条件的行
DELETE FROM employees WHERE hire_date < '2020-01-01';
-- 使用 JOIN 进行条件删除
DELETE e FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id
WHERE d.status = 'inactive';
安全提示: - UPDATE 和 DELETE 前先用 SELECT 验证条件 - 大批量 DML 操作应该分批进行,避免锁表过久 - 重要操作前做好备份和回滚计划
常用函数
字符串函数
| 函数 | 示例 | 结果 |
|---|---|---|
| CONCAT() | CONCAT('Hello', ' ', 'World') |
'Hello World' |
| SUBSTR() | SUBSTR('MySQL', 1, 2) |
'My' |
| LENGTH() | LENGTH('MySQL') |
5 |
| UPPER() | UPPER('mysql') |
'MYSQL' |
| LOWER() | LOWER('MYSQL') |
'mysql' |
| TRIM() | TRIM(' hello ') |
'hello' |
| REPLACE() | REPLACE('foo bar', 'bar', 'baz') |
'foo baz' |
日期函数
| 函数 | 示例 | 说明 |
|---|---|---|
| NOW() | NOW() |
当前日期时间 |
| DATE() | DATE(NOW()) |
提取日期部分 |
| DATE_ADD() | DATE_ADD(NOW(), INTERVAL 7 DAY) |
日期加法 |
| DATE_DIFF() | DATEDIFF('2024-12-31', '2024-01-01') |
两日期差(天数) |
| YEAR/MONTH/DAY() | YEAR(NOW()) |
提取年/月/日 |
数学函数
| 函数 | 示例 |
|---|---|
| ROUND() | ROUND(3.14159, 2) → 3.14 |
| FLOOR() / CEIL() | FLOOR(3.9) → 3 |
| ABS() | ABS(-5) → 5 |
| POWER() / SQRT() | POWER(2, 3) → 8 |
性能优化要点
索引友好的查询
- 在 WHERE 子句的列上建立索引
- 避免在索引列上使用函数:
❌ WHERE YEAR(date) = 2024→✓ WHERE date >= '2024-01-01' AND date < '2025-01-01' - 使用 BETWEEN 而不是多个 OR:
❌ salary = 3000 OR salary = 4000 OR salary = 5000→✓ salary BETWEEN 3000 AND 5000
避免全表扫描
- 用 LIMIT 限制结果集大小
- 在大表上的 LIKE 查询只在右侧使用通配符:
name LIKE 'John%' - 避免
SELECT *,只查询需要的列
优化 JOIN 和子查询
- 用 JOIN 替代子查询(特别是关联子查询)
- 用 EXISTS 替代 IN(当右侧是子查询时)
- 先 WHERE 过滤,再 JOIN
常见问题
Q1: NULL 值如何处理?
在 MySQL 中,NULL 代表"未知"或"无值"。比较 NULL 时要用 IS NULL 而不是 = NULL: ✓ WHERE email IS NULL ❌ WHERE email = NULL 也可以用 COALESCE() 或 IFNULL() 为 NULL 提供默认值: SELECT name, COALESCE(phone, 'No Phone') FROM users;
Q2: UNION 和 UNION ALL 的区别?
UNION 会去除重复行(成本高),UNION ALL 保留所有行(更快)。如果你知道没有重复,用 UNION ALL。
Q3: IN vs EXISTS 哪个更快?
当右侧是子查询时,EXISTS 通常更快,因为它只检查是否存在匹配的行。IN 则需要构建完整的子查询结果集。
Q4: 为什么 GROUP BY 中的列必须在 SELECT 中?
MySQL 5.7 后的"严格"模式要求 SELECT 中的非聚合列都在 GROUP BY 中。这防止了模糊的结果。如果某列不需要分组,把它放在聚合函数中(如 MAX(column))。
Q5: 如何找出慢查询?
用 EXPLAIN 分析查询执行计划,检查是否全表扫描、是否使用了索引。在 MySQL 命令行执行: EXPLAIN SELECT * FROM employees WHERE salary > 5000; 关注 type 列:ALL(全表扫描)、index(全索引扫描)、range(范围)、ref(精确匹配)、const(常数)。