MySQL从入门到精通(第三篇):子查询与视图的深度应用

在上一篇中,我们掌握了分组统计和多表关联的核心技巧。但面对复杂的数据场景时,如何通过子查询穿透多层逻辑,或借助视图抽象业务模型,才是构建企业级数据库的进阶之路。本篇将深入讲解子查询的嵌套艺术、视图的创建与优化,并通过实战案例展示其威力!


一、子查询:穿透数据逻辑的瑞士军刀

1.1 四种子查询类型与应用场景

类型 特点 典型场景示例
标量子查询 返回单值(如SELECT 1 比较单个值:WHERE salary > (SELECT AVG(salary) FROM employees)
行子查询 返回一行数据 多列匹配:WHERE (dept, role) IN (SELECT dept, role FROM privileges)
列子查询 返回一列数据 多值过滤:WHERE id IN (SELECT customer_id FROM orders)
表子查询 返回多行多列(临时表) 复杂关联:FROM (SELECT * FROM orders WHERE amount > 1000) AS big_orders

示例:标量子查询计算部门平均工资

SELECT name, salary 
FROM employees 
WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = 'Sales');

1.2 子查询的优化陷阱与解决方案

  • 性能黑洞:避免在WHERE子句中使用NOT IN子查询(如WHERE id NOT IN (SELECT customer_id FROM orders)),改用LEFT JOIN+IS NULL更高效。
  • 关联子查询优化:对于需遍历主表的关联子查询(如WHERE EXISTS (SELECT 1 FROM orders WHERE orders.customer_id = customers.id) ),确保子查询字段有索引。
  • 子查询转联结:将WHERE IN转换为INNER JOIN可提升性能,例如:
    -- 低效写法 
    SELECT * FROM customers 
    WHERE id IN (SELECT customer_id FROM orders);
    
    -- 优化写法 
    SELECT c.* 
    FROM customers c 
    INNER JOIN orders o ON c.id  = o.customer_id; 

二、视图:数据抽象的魔法镜像

2.1 视图的定义与核心价值

视图(View)是虚拟表,其数据来自查询结果,具有以下特性:

  • 逻辑抽象:将复杂查询封装为虚拟表,简化业务逻辑
  • 权限控制:通过视图限制用户访问敏感数据
  • 动态更新:基于基础表实时刷新数据

示例:创建员工部门视图

CREATE VIEW employee_department_view AS 
SELECT e.name,  e.salary,  d.department_name,  d.location  
FROM employees e 
INNER JOIN departments d ON e.department_id  = d.id; 

2.2 视图的更新限制与优化技巧

  • 可更新视图条件
    • 仅引用单张基表
    • 不包含聚合函数、DISTINCTGROUP BYHAVING
    • 主键需完整映射
  • 性能优化策略
    -- 为视图添加索引(MySQL不支持直接为视图建索引,需在基表操作)
    CREATE INDEX idx_employee_department ON employees(department_id);
  • 物化视图替代方案:通过定期刷新的临时表实现高效查询

三、实战案例:子查询与视图的协同作战

案例1:统计高价值客户

需求:找出订单总金额超过部门平均值的客户

-- 方法一:使用子查询 
SELECT c.customer_name,  SUM(o.amount)  AS total_amount 
FROM customers c 
INNER JOIN orders o ON c.id  = o.customer_id  
GROUP BY c.customer_name  
HAVING total_amount > (SELECT AVG(total_amount) 
                       FROM (SELECT SUM(amount) AS total_amount 
                             FROM orders 
                             GROUP BY customer_id) AS dept_avg);
 
-- 方法二:通过视图简化 
CREATE VIEW customer_orders AS 
SELECT customer_id, SUM(amount) AS total_amount 
FROM orders 
GROUP BY customer_id;
 
SELECT c.name,  vo.total_amount  
FROM customers c 
INNER JOIN customer_orders vo ON c.id  = vo.customer_id  
WHERE vo.total_amount  > (SELECT AVG(total_amount) FROM customer_orders);

案例2:安全敏感数据访问

需求:为财务部门创建仅显示工资的视图,隐藏其他敏感信息

CREATE VIEW finance_employee_view AS 
SELECT name, salary, department 
FROM employees 
WHERE department = 'Finance';
 
-- 限制用户权限 
GRANT SELECT ON finance_employee_view TO 'finance_user'@'%';

四、实战题目:检验你的SQL功力

题目1:查询比公司平均工资高的部门
解答

SELECT department, AVG(salary) AS avg_salary 
FROM employees 
GROUP BY department 
HAVING avg_salary > (SELECT AVG(salary) FROM employees);

题目2:创建包含订单详情的视图并统计
解答

-- 创建视图 
CREATE VIEW order_details_view AS 
SELECT o.id,  o.amount,  c.customer_name,  p.product_name  
FROM orders o 
INNER JOIN customers c ON o.customer_id  = c.id  
INNER JOIN products p ON o.product_id  = p.id; 
 
-- 统计每个客户的订单数 
SELECT customer_name, COUNT(*) AS order_count 
FROM order_details_view 
GROUP BY customer_name;

五、下篇预告:存储过程与索引优化

下一期我们将深入探讨:

  • 存储过程的编写与事务控制
  • 索引的类型选择与性能分析
  • 复杂查询的执行计划解读
  • 高并发场景下的锁机制

关注我,持续解锁数据库高阶技能!
如果本文对你有帮助,欢迎点赞、收藏、转发,让我们共同构建高效可靠的数据库系统!

扫描二维码关注公众号,回复: 17576926 查看本文章