视图、函数和过程:MySQL数据库核心工具详解
上一节内容中,我们已经学习了DML(数据操纵语言)的插入、删除与更新操作。本节课,我们将继续围绕「员工管理系统数据库(hrs)」展开,深入拆解视图、函数和过程这三大MySQL常用数据库对象的用法。它们既能实现数据隐藏、功能封装与复杂操作简化,也是开发人员提升开发效率、保障数据安全的关键工具。
温馨提示:在执行本文所有操作前,请务必确认已完成「建库建表的DDL语句」和「插入数据的DML语句」,确保表结构与数据完整无误,再通过以下命令切换至hrs数据库,避免出现操作报错。
-- 切换到hrs数据库(``符号用于规避关键字冲突,建议日常开发规范使用)USE `hrs`;
一、视图:虚拟表的实用技巧
视图是关系型数据库中,将一组查询指令所构成的结果集,组合成可直接查询的数据表对象。简单来说,视图本质上是一张虚拟表——与实体数据表不同,它没有实际的存储结构,更像是一条被赋予名称、永久保存于数据库中的SQL查询语句。
在实际开发中,合理使用视图能带来不少便利,具体优势如下:
- 1. 隐藏实体表结构,降低数据库安全风险:将核心数据表的细节隐藏,仅向外部程序开放视图中的部分字段,避免数据结构泄露,减少被攻击的可能性。
- 2. 限制数据修改权限,保障数据安全:多数情况下视图仅支持读取操作,更新视图的操作存在诸多限制,可有效防止外部程序通过视图随意修改核心数据。
- 3. 复用复杂SQL,提升开发效率:将逻辑复杂的查询语句封装为视图,后续无需重复编写复杂代码,直接访问视图即可获取所需数据,也可将视图与实体表结合进行连接查询。
- 4. 格式化数据输出,适配业务需求:创建视图时,可对查询结果进行格式化处理,让视图返回的数据格式更贴合实际业务场景,无需额外编写格式化代码。
1. 如何创建视图
核心语法:CREATE VIEW 视图名称 AS 具体查询语句;其中,查询语句可根据业务需求筛选字段、添加查询条件,实现虚拟表的个性化定制。
createview `vw_emp_simple`asselect `eno`,`ename`,`job`,`dno`from `tb_emp`;
温馨提示:由于视图不存储实际数据,每次访问视图时,数据库都会重新执行其对应的查询语句来获取数据。如果视图是基于连接查询、嵌套查询创建的,可能会出现查询性能下降的情况,建议使用前先进行性能测试,确保满足业务需求。
创建上述视图后,我们可以借助之前学习的DCL(数据控制语言),限制部分用户仅能通过该视图获取员工信息,从而避免员工表中工资(sal)、补贴(comm)等敏感字段泄露。以下代码演示了如何从视图中查询数据:
select*from `vw_emp_simple`;
查询结果如下:
+------+-----------+--------------+-----+| eno | ename | job | dno |+------+-----------+--------------+-----+| 1359 | 胡二刀 | 销售员 | 30 || 2056 | 乔峰 | 分析师 | 20 || 3088 | 李莫愁 | 设计师 | 20 || 3211 | 张无忌 | 程序员 | 20 || 3233 | 丘处机 | 程序员 | 20 || 3244 | 欧阳锋 | 程序员 | 20 || 3251 | 张翠山 | 程序员 | 20 || 3344 | 黄蓉 | 销售主管 | 30 || 3577 | 杨过 | 会计 | 10 || 3588 | 朱九真 | 会计 | 10 || 4466 | 苗人凤 | 销售员 | 30 || 5234 | 郭靖 | 出纳 | 10 || 5566 | 宋远桥 | 会计师 | 10 || 7800 | 张三丰 | 总裁 | 20 |+------+-----------+--------------+-----+
2. 视图的可更新性说明
很多同学会疑问:既然视图是一张虚拟表,那它里面的数据可以更新吗?答案是:视图的可更新性需结合具体场景判断,以下4种类型的视图无法进行更新操作:
- 1. 包含聚合函数(
SUM、MIN、MAX、AVG、COUNT等)、DISTINCT、GROUP BY、HAVING、UNION或UNION ALL的视图。 - 4.
WHERE子句的子查询,引用了FROM子句中数据表的视图。
3. 视图的删除方法
核心语法:DROP VIEW IF EXISTS 视图名称;该语句可避免视图不存在时出现报错,同时支持视图更新——既可以先删除旧视图再重新创建,也可直接使用CREATE OR REPLACE VIEW语句更新。
dropview if exists `vw_emp_simple`;
补充说明:若需更新视图,除了先删除再创建的方式,也可直接使用create or replace view语句,无需删除旧视图,操作更便捷。
4. 视图的使用规则与限制
- 1. 视图支持嵌套使用:可利用现有视图检索的数据,创建新的视图;同时,视图也可与实体数据表结合进行各类查询操作。
- 2.
order by子句的使用限制:创建视图时可添加order by子句,但如果查询视图时也使用了order by,则视图中原有的order by会被覆盖。 - 3. 索引与触发器相关限制:视图无法使用索引,也不会触发触发器的执行(实际开发中,出于性能等多方面考虑,通常不建议使用触发器,本文暂不展开讲解)。
二、函数:MySQL中的功能封装工具
MySQL中的函数,与Python等编程语言中的函数逻辑大致相同,核心作用都是封装功能独立、可重复使用的代码片段。两者的主要区别在于,MySQL中的函数可直接执行SQL语句,能更便捷地适配数据库操作场景。
下面我们通过一个实际案例,演示如何自定义函数——实现超长字符串的截断功能,该函数可直接在查询中调用,大幅提升代码复用性。
1. 自定义函数的创建方法
核心语法:先通过DELIMITER修改语句定界符 → 再使用CREATE FUNCTION定义函数(包含参数、返回值类型、函数属性)→ 编写函数体 → 最后恢复默认定界符。
delimiter $$createfunction fn_truncate_string(content varchar(10000),max_length int unsigned) returnsvarchar(10000) nosqlbegindeclareresultvarchar(10000) default content;if char_length(content) > max_length thensetresult=left(content, max_length);setresult= concat(result, '……');end if;returnresult;end $$delimiter ;
说明1:函数声明后的no sql,表示该函数体中未使用SQL语句;若函数体需要通过SQL语句读取数据,则需将其声明为reads sql data。
说明2:定义函数前后修改定界符(DELIMITER命令),是为了避免函数体中以;结尾的语句被提前截断执行——默认情况下,MySQL以;作为语句终止符,不修改定界符会导致函数定义失败。
2. 自定义函数的调用方式
核心语法:在SELECT语句中,像使用MySQL系统函数(如CONCAT、LEFT)一样调用自定义函数,可搭配AS关键字设置别名,让查询结果更易读。
select fn_truncate_string('和我在成都的街头走一走,直到所有的灯都熄灭了也不停留', 10) as short_string;
查询结果如下:
+--------------------------------------+| short_string |+--------------------------------------+| 和我在成都的街头走一…… |+--------------------------------------+
三、过程:复杂SQL操作的封装方案
过程(又称存储过程),是将一组SQL语句提前编译好,存储在数据库中的集合。调用过程不仅能简化应用程序开发人员的工作,减少与数据库服务器的通信次数,还能有效提升数据操作的性能。
在此之前,我们使用的SQL语句均为针对单个或多个数据表的单条操作,但实际开发中,很多业务场景需要多条SQL语句配合才能完成。例如,电商网站受理用户订单时,需执行以下一系列操作:
- 1. 通过查询核对目标商品的库存是否存在、数量是否充足;
- 2. 若库存充足,锁定对应库存(防止重复售卖),并减少库存数量,确保库存数据准确;
- 3. 若库存不足,需与供应商对接补货,或生成系统提示消息,告知用户无法下单;
- 4. 无论订单受理成功与否,都需生成操作流水记录,并向用户发送相关通知。
将这类复杂操作封装为过程,不仅能保证数据一致性(避免部分操作成功、部分失败),后续业务变动时,只需修改过程内部逻辑,无需修改调用端代码。同时,过程不会暴露数据表的核心细节,且执行速度比逐条执行SQL语句更快。
1. 存储过程的创建方法
核心语法:与自定义函数类似,需先修改定界符,过程中可包含变量、条件判断、事务、异常处理等逻辑,与函数的核心区别是:过程无返回值,主要用于执行一系列操作。
下面我们创建一个存储过程,实现hrs数据库中员工工资的普调功能,具体规则为:10部门员工薪资上浮300,20部门员工薪资上浮800,30部门员工薪资上浮500。
delimiter $$createprocedure sp_upgrade_salary()begindeclare flag booleandefault1;-- 定义异常处理器,捕获SQL执行异常declare continue handler forsqlexceptionset flag=0;-- 开启事务环境,确保操作的原子性start transaction;update tb_emp set sal=sal+300where dno=10;update tb_emp set sal=sal+800where dno=20;update tb_emp set sal=sal+500where dno=30;-- 根据异常情况,执行提交或回滚操作if flag thencommit;elserollback;end if;end $$delimiter ;
补充说明:上述过程中使用start transaction开启了事务环境(事务相关知识将在本文后续补充)。为了应对SQL执行过程中可能出现的异常,我们定义了flag变量和异常处理器——若执行过程中出现异常,flag会被赋值为0,后续根据flag的值判断执行事务提交(commit)或回滚(rollback),确保数据一致性。
2. 存储过程的调用方式
核心语法:CALL 过程名称();无需指定返回值,调用后会自动执行过程中封装的所有SQL操作。
call sp_upgrade_salary();
3. 存储过程的删除方法
核心语法:DROP PROCEDURE IF EXISTS 过程名称;与视图、函数的删除逻辑一致,可避免过程不存在时出现报错。
dropprocedure if exists sp_upgrade_salary;
4. 存储过程的使用注意事项
存储过程的功能十分强大,除了上述示例中的逻辑,还可在过程中定义变量、条件判断、循环语句,通过游标操作查询结果,甚至使用事件调度器等。这些高级用法,本文暂不展开,感兴趣的同学可自行深入学习。
需要特别提醒的是:虽然存储过程有诸多优势,但在实际开发中,不建议频繁使用,更不建议将大量复杂运算放入过程中。因为数据库往往是系统的性能瓶颈,过多的过程操作会给数据库服务器带来巨大压力,加剧性能问题。对于互联网产品开发,建议让数据库专注于数据存储,将复杂运算和业务处理交给应用服务器,若应用服务器压力过大,可通过部署多台服务器分摊压力,更易实现扩展。
如果大家对视图、函数、过程,以及未提及的触发器等知识感兴趣,建议阅读MySQL入门书籍《MySQL必知必会》进行基础了解即可——这些知识点在日常开发中未必常用,很多时候只是为了应对面试需求。
四、补充知识点:范式、数据完整性与一致性
结合本节课讲解的视图、函数、过程示例,我们补充3个MySQL数据库设计与数据安全相关的核心知识点,帮助大家更深入理解数据库操作的底层逻辑和规范。
1. 范式理论:数据库表设计的指导原则
范式理论是关系型数据库二维表设计的核心指导思想,核心目标是减少数据冗余、保证数据一致性,日常开发中最常用的是前三范式:
- 1. 第一范式(1NF):数据表的每一列值域,都由不可分割的原子值组成,不能存在多值、复合值。
- 2. 第二范式(2NF):数据表中的所有数据,都需与该表的键(主键或候选键)完全依赖,不能存在部分依赖。
- 3. 第三范式(3NF):所有非键属性,仅与候选键存在相关性,非键属性之间不能存在依赖关系。
补充说明:实际开发中,出于操作效率的考虑,往往会采用反范式设计——故意降低范式级别,增加适量数据冗余,以此提升查询和操作性能,平衡规范性与效率。
2. 数据完整性:确保数据的准确与有效
数据完整性是指数据库中的数据,在逻辑上保持一致性、准确性和有效性,主要分为三类,具体如下:
- 1. 实体完整性:确保每个实体都是唯一的,不会出现重复数据。
- • 核心实现方式:主键(
primary key)、唯一约束(unique)。
- 2. 引用完整性(又称参照完整性):确保关系中不允许引用不存在的实体,避免出现无效关联。
- • 核心实现方式:外键(
foreign key)。
- 3. 域完整性:确保数据符合预设规则,是有效的数据。
- • 核心实现方式:指定数据类型及长度、非空约束(
not null)、默认值约束(default)、检查约束(check)。
补充说明:在MySQL 8.x版本之前,检查约束(check)仅能声明,无法实际生效;MySQL 8.x及以上版本,才真正支持检查约束的功能。
3. 数据一致性:事务的核心作用
数据一致性是指事务执行前后,数据库的状态始终保持一致,核心依赖于事务及其ACID特性,具体拆解如下:
- 1. 事务的定义:一系列对数据库的读/写操作的集合,这些操作要么全部执行成功,要么全部执行失败,不会出现部分成功、部分失败的情况。
- • 原子性(Atomicity):事务是一个不可分割的整体,包含的所有操作要么全部执行,要么全部不执行,无中间状态。
- • 一致性(Consistency):事务执行前后,数据库的状态从一个一致状态,转变为另一个一致状态,数据不会出现异常。
- • 隔离性(Isolation):多个事务并发执行时,每个事务的执行过程相互独立,不会受到其他事务的干扰。
- • 持久性(Durability):事务一旦提交,对数据库的修改将永久保存,即使数据库发生故障,修改后的数据也不会丢失。
start transaction
commit
rollback
show variables like'transaction_isolation';
查询结果如下:
+-----------------------+-----------------+| Variable_name | Value |+-----------------------+-----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+-----------------+
从结果可以看出,MySQL默认的事务隔离级别是REPEATABLE-READ(可重复读)。
set session transaction isolation level read committed;
修改后重新查看事务隔离级别,结果如下:
+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+
补充说明:关系型数据库的事务是一个复杂的话题,当多个事务并发访问数据时,可能会出现三类读数据问题(脏读、不可重复读、幻读)和两类更新数据问题(第一类丢失更新、第二类丢失更新)。若想深入了解这五类问题,可阅读CSDN网站上《Java面试题全集(上)》一文的第80题。
为了避免上述问题,关系型数据库底层提供了锁机制:按锁定对象可分为表级锁和行级锁,按并发事务锁定关系可分为共享锁和独占锁。直接使用锁操作繁琐,因此数据库提供了自动锁机制——只需指定合适的事务隔离级别,数据库会自动分析SQL语句,为事务访问的资源添加合适的锁,并通过多种方式维护锁的性能,这一过程对用户完全透明。
若想深入学习MySQL事务和锁的细节知识,推荐阅读进阶书籍《高性能MySQL》,该书是数据库领域的经典读物,能帮助大家系统掌握MySQL性能优化与底层原理。
ANSI/ISO SQL 92标准定义了4个等级的事务隔离级别,需要注意的是:事务隔离级别与数据访问的并发性呈反比——隔离级别越高,数据一致性越好,但并发性越差;反之,隔离级别越低,并发性越好,但数据一致性风险越高。实际开发中,需结合具体业务场景选择合适的隔离级别,没有统一的万能原则。
五、总结
MySQL的知识体系十分庞大,本文讲解的视图、函数、过程,以及补充的范式、数据完整性、事务等知识点,仅为MySQL的基础核心内容。除此之外,MySQL性能调优、运维工具使用、数据备份与恢复、服务监控、高可用架构部署等内容,由于篇幅限制,本文无法逐一展开。
后续有相关需求时,我们再针对性讲解这些进阶内容,也建议大家在日常学习中多动手实践、自行探索,逐步积累MySQL开发与运维经验。