MariaDB高级查询 #
一、子查询 #
1.1 子查询概述 #
sql
-- 子查询:嵌套在其他查询中的查询
-- 可以出现在SELECT、FROM、WHERE、HAVING子句中
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE status = 'completed');
1.2 标量子查询 #
sql
-- 返回单个值的子查询
SELECT
name,
(SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS order_count
FROM users;
-- 在WHERE中使用
SELECT * FROM users
WHERE balance > (SELECT AVG(balance) FROM users);
1.3 列子查询 #
sql
-- 返回一列值的子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);
-- 使用ANY/SOME
SELECT * FROM users
WHERE id = ANY (SELECT user_id FROM orders WHERE status = 'completed');
-- 使用ALL
SELECT * FROM users
WHERE balance > ALL (SELECT balance FROM users WHERE status = 0);
1.4 行子查询 #
sql
-- 返回一行多列
SELECT * FROM users
WHERE (id, status) = (SELECT user_id, status FROM orders WHERE id = 1);
1.5 表子查询 #
sql
-- 返回多行多列(派生表)
SELECT u.name, o.order_count
FROM users u
JOIN (
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
) o ON u.id = o.user_id;
1.6 相关子查询 #
sql
-- 相关子查询:引用外部查询的表
SELECT
name,
(SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
FROM users u;
-- EXISTS子查询
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id AND o.status = 'completed'
);
-- NOT EXISTS
SELECT * FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM orders o WHERE o.user_id = u.id
);
二、CTE(公用表表达式) #
2.1 基本CTE #
sql
-- MariaDB 10.2+ 支持CTE
WITH user_orders AS (
SELECT user_id, COUNT(*) AS order_count
FROM orders
GROUP BY user_id
)
SELECT u.name, uo.order_count
FROM users u
LEFT JOIN user_orders uo ON u.id = uo.user_id;
2.2 多个CTE #
sql
WITH
active_users AS (
SELECT * FROM users WHERE status = 1
),
user_orders AS (
SELECT user_id, SUM(amount) AS total_amount
FROM orders
GROUP BY user_id
)
SELECT au.name, uo.total_amount
FROM active_users au
LEFT JOIN user_orders uo ON au.id = uo.user_id;
2.3 递归CTE #
sql
-- 递归CTE:处理层次结构数据
CREATE TABLE categories (
id INT PRIMARY KEY,
name VARCHAR(50),
parent_id INT
);
INSERT INTO categories VALUES
(1, 'Electronics', NULL),
(2, 'Computers', 1),
(3, 'Phones', 1),
(4, 'Laptops', 2),
(5, 'Desktops', 2);
-- 查询所有子分类
WITH RECURSIVE category_tree AS (
-- 基础查询
SELECT id, name, parent_id, 1 AS level, CAST(name AS CHAR(500)) AS path
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- 递归查询
SELECT c.id, c.name, c.parent_id, ct.level + 1, CONCAT(ct.path, ' > ', c.name)
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY path;
+----+------------+-----------+-------+---------------------------+
| id | name | parent_id | level | path |
+----+------------+-----------+-------+---------------------------+
| 1 | Electronics| NULL | 1 | Electronics |
| 2 | Computers | 1 | 2 | Electronics > Computers |
| 4 | Laptops | 2 | 3 | Electronics > Computers > Laptops |
| 5 | Desktops | 2 | 3 | Electronics > Computers > Desktops |
| 3 | Phones | 1 | 2 | Electronics > Phones |
+----+------------+-----------+-------+---------------------------+
2.4 递归CTE示例 #
sql
-- 生成数字序列
WITH RECURSIVE numbers AS (
SELECT 1 AS n
UNION ALL
SELECT n + 1 FROM numbers WHERE n < 10
)
SELECT * FROM numbers;
-- 计算阶乘
WITH RECURSIVE factorial AS (
SELECT 1 AS n, 1 AS fact
UNION ALL
SELECT n + 1, fact * (n + 1) FROM factorial WHERE n < 5
)
SELECT * FROM factorial;
三、窗口函数 #
3.1 窗口函数概述 #
sql
-- MariaDB 10.2+ 支持窗口函数
-- 语法:函数名() OVER (PARTITION BY ... ORDER BY ...)
SELECT
name,
age,
ROW_NUMBER() OVER (ORDER BY age DESC) AS rank
FROM users;
3.2 排名函数 #
sql
-- ROW_NUMBER:连续排名
SELECT
name,
score,
ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num
FROM students;
-- RANK:并列排名,跳过后续名次
SELECT
name,
score,
RANK() OVER (ORDER BY score DESC) AS rank
FROM students;
-- DENSE_RANK:并列排名,不跳过后续名次
SELECT
name,
score,
DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank
FROM students;
-- 对比示例
+-------+-------+----------+------+------------+
| name | score | row_num | rank | dense_rank |
+-------+-------+----------+------+------------+
| Alice | 95 | 1 | 1 | 1 |
| Bob | 90 | 2 | 2 | 2 |
| Carol | 90 | 3 | 2 | 2 |
| Dave | 85 | 4 | 4 | 3 |
+-------+-------+----------+------+------------+
3.3 分区排名 #
sql
-- 按班级排名
SELECT
name,
class,
score,
ROW_NUMBER() OVER (PARTITION BY class ORDER BY score DESC) AS class_rank
FROM students;
+-------+-------+-------+------------+
| name | class | score | class_rank |
+-------+-------+-------+------------+
| Alice | A | 95 | 1 |
| Bob | A | 90 | 2 |
| Carol | B | 92 | 1 |
| Dave | B | 88 | 2 |
+-------+-------+-------+------------+
3.4 聚合窗口函数 #
sql
-- 累计求和
SELECT
order_date,
amount,
SUM(amount) OVER (ORDER BY order_date) AS running_total
FROM orders;
-- 移动平均
SELECT
order_date,
amount,
AVG(amount) OVER (
ORDER BY order_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_avg
FROM orders;
-- 分区聚合
SELECT
name,
department,
salary,
AVG(salary) OVER (PARTITION BY department) AS dept_avg
FROM employees;
3.5 偏移函数 #
sql
-- LAG:获取前一行的值
SELECT
order_date,
amount,
LAG(amount) OVER (ORDER BY order_date) AS prev_amount,
amount - LAG(amount) OVER (ORDER BY order_date) AS diff
FROM orders;
-- LEAD:获取后一行的值
SELECT
order_date,
amount,
LEAD(amount) OVER (ORDER BY order_date) AS next_amount
FROM orders;
-- 指定偏移量和默认值
SELECT
order_date,
amount,
LAG(amount, 2, 0) OVER (ORDER BY order_date) AS prev_2_amount
FROM orders;
3.6 首尾函数 #
sql
-- FIRST_VALUE:分区第一个值
SELECT
name,
department,
salary,
FIRST_VALUE(salary) OVER (PARTITION BY department ORDER BY salary DESC) AS dept_max
FROM employees;
-- LAST_VALUE:分区最后一个值
SELECT
name,
department,
salary,
LAST_VALUE(salary) OVER (
PARTITION BY department
ORDER BY salary DESC
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS dept_min
FROM employees;
3.7 NTH_VALUE #
sql
-- 获取第N个值
SELECT
name,
score,
NTH_VALUE(name, 2) OVER (ORDER BY score DESC) AS second_place
FROM students;
3.8 NTILE #
sql
-- 将数据分成N组
SELECT
name,
score,
NTILE(4) OVER (ORDER BY score DESC) AS quartile
FROM students;
四、CASE表达式 #
4.1 简单CASE #
sql
SELECT
name,
status,
CASE status
WHEN 1 THEN 'Active'
WHEN 0 THEN 'Inactive'
ELSE 'Unknown'
END AS status_name
FROM users;
4.2 搜索CASE #
sql
SELECT
name,
score,
CASE
WHEN score >= 90 THEN 'A'
WHEN score >= 80 THEN 'B'
WHEN score >= 70 THEN 'C'
WHEN score >= 60 THEN 'D'
ELSE 'F'
END AS grade
FROM students;
4.3 CASE在聚合中 #
sql
-- 条件计数
SELECT
COUNT(*) AS total,
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS active_count,
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS inactive_count
FROM users;
-- 条件求和
SELECT
SUM(CASE WHEN status = 1 THEN balance ELSE 0 END) AS active_balance,
SUM(CASE WHEN status = 0 THEN balance ELSE 0 END) AS inactive_balance
FROM users;
4.4 CASE在ORDER BY中 #
sql
-- 自定义排序
SELECT * FROM users
ORDER BY
CASE status
WHEN 1 THEN 1
WHEN 0 THEN 2
ELSE 3
END,
name;
五、高级JOIN #
5.1 自连接 #
sql
-- 查找同一部门的其他员工
SELECT
e1.name AS employee,
e2.name AS colleague
FROM employees e1
JOIN employees e2 ON e1.department_id = e2.department_id AND e1.id != e2.id;
-- 查找经理-员工关系
SELECT
e.name AS employee,
m.name AS manager
FROM employees e
LEFT JOIN employees m ON e.manager_id = m.id;
5.2 交叉连接 #
sql
-- 笛卡尔积
SELECT u.name, p.product_name
FROM users u
CROSS JOIN products p;
-- 生成所有组合
SELECT d.date, p.product_id
FROM dates d
CROSS JOIN products p;
5.3 外连接 #
sql
-- LEFT JOIN:左表所有记录
SELECT u.name, o.order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id;
-- RIGHT JOIN:右表所有记录
SELECT u.name, o.order_id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
-- FULL OUTER JOIN(MariaDB不支持,使用UNION模拟)
SELECT u.name, o.order_id
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
UNION
SELECT u.name, o.order_id
FROM users u
RIGHT JOIN orders o ON u.id = o.user_id;
六、集合操作 #
6.1 UNION #
sql
-- 合并结果(去重)
SELECT name FROM users
UNION
SELECT name FROM customers;
-- UNION ALL(不去重)
SELECT name FROM users
UNION ALL
SELECT name FROM customers;
6.2 INTERSECT #
sql
-- MariaDB 10.3+ 支持INTERSECT
-- 交集
SELECT name FROM users
INTERSECT
SELECT name FROM customers;
-- 模拟INTERSECT(旧版本)
SELECT name FROM users
WHERE name IN (SELECT name FROM customers);
6.3 EXCEPT #
sql
-- MariaDB 10.3+ 支持EXCEPT
-- 差集
SELECT name FROM users
EXCEPT
SELECT name FROM customers;
-- 模拟EXCEPT(旧版本)
SELECT name FROM users
WHERE name NOT IN (SELECT name FROM customers);
七、高级分组 #
7.1 GROUPING SETS #
sql
-- MariaDB 10.2+ 支持GROUPING SETS
SELECT
COALESCE(department, 'All') AS department,
COALESCE(job_title, 'All') AS job_title,
COUNT(*) AS count
FROM employees
GROUP BY GROUPING SETS (
(department, job_title),
(department),
(job_title),
()
);
7.2 ROLLUP #
sql
-- 生成分组汇总
SELECT
department,
job_title,
COUNT(*) AS count
FROM employees
GROUP BY department, job_title WITH ROLLUP;
-- 等价于
SELECT department, job_title, COUNT(*) FROM employees GROUP BY department, job_title
UNION ALL
SELECT department, NULL, COUNT(*) FROM employees GROUP BY department
UNION ALL
SELECT NULL, NULL, COUNT(*) FROM employees;
7.3 CUBE #
sql
-- MariaDB 10.2+ 支持CUBE
-- 生成所有组合的汇总
SELECT
department,
job_title,
COUNT(*) AS count
FROM employees
GROUP BY CUBE (department, job_title);
八、高级过滤 #
8.1 TOP N查询 #
sql
-- 每组前N条
WITH ranked AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) AS rn
FROM employees
)
SELECT * FROM ranked WHERE rn <= 3;
8.2 分页优化 #
sql
-- 传统分页(大偏移量性能差)
SELECT * FROM users ORDER BY id LIMIT 10000, 10;
-- 优化:使用WHERE条件
SELECT * FROM users
WHERE id > 10000
ORDER BY id
LIMIT 10;
-- 使用JOIN优化
SELECT u.* FROM users u
JOIN (SELECT id FROM users ORDER BY id LIMIT 10000, 10) t
ON u.id = t.id;
8.3 去重查询 #
sql
-- 查找重复数据
SELECT name, COUNT(*)
FROM users
GROUP BY name
HAVING COUNT(*) > 1;
-- 删除重复数据保留一条
DELETE u1 FROM users u1
JOIN users u2 ON u1.name = u2.name AND u1.id > u2.id;
九、总结 #
高级查询要点:
| 技术 | 说明 | 版本要求 |
|---|---|---|
| 子查询 | 嵌套查询 | 全部 |
| CTE | 公用表表达式 | 10.2+ |
| 递归CTE | 层次结构处理 | 10.2+ |
| 窗口函数 | 排名、聚合、偏移 | 10.2+ |
| CASE表达式 | 条件表达式 | 全部 |
| GROUPING SETS | 多维分组 | 10.2+ |
最佳实践:
- 复杂查询使用CTE提高可读性
- 层次数据使用递归CTE
- 排名计算使用窗口函数
- 大偏移量分页需要优化
- 使用EXPLAIN分析查询计划
下一步,让我们学习多表查询!
最后更新:2026-03-27