第 2 章

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

性能优化要点

索引友好的查询

避免全表扫描

优化 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(常数)。

本章评分
4.6  / 5  (114 评分)

💬 留言讨论