# 视图函数与过程

# 视图

在 MySQL 中,视图(View)是一个虚拟的表,其内容由查询定义。视图的内容并不在数据库中实际存储,而是存储在数据字典中。当用户对视图进行查询时,数据库会动态地执行定义视图的 SELECT 语句,并返回结果。视图可以包含表中的所有行,也可以只包含满足特定条件的行。

  • 虚拟表:视图是基于 SQL 语句的结果集的可视化表示,它本身不存储数据,而是根据查询动态生成数据。

  • 简化复杂查询:通过创建视图,可以将复杂的 SQL 查询封装起来,使得用户可以通过查询视图来间接执行复杂的查询,从而简化数据检索操作。

  • 增加数据安全性:通过视图,可以控制用户对数据的访问权限。例如,可以创建一个只包含某些列或行的视图,并授权用户查询该视图,而不是直接查询表,从而保护表中的数据不被未授权访问。

  • 逻辑数据独立性:当数据库表结构发生变化时(如增加列、删除列或修改列名),只要视图定义的查询能够正确执行,用户通过视图查询数据就不会受到影响。这提高了数据库的逻辑数据独立性。

# 创建视图

CREATE VIEW 视图名称 AS  
SELECT 列名称  
FROM 表名称  
WHERE 条件;


CREATE VIEW high_salaries AS  
SELECT name, salary  
FROM employees  
WHERE salary > 5000;

# 使用视图

SELECT * FROM high_salaries;

# 更新视图

虽然视图是基于查询的虚拟表,但在某些情况下,也可以对视图进行更新操作(如插入、更新、删除)。然而,并非所有视图都是可更新的,视图的更新操作受到以下条件的限制:

  • 视图中包含的列必须是表中唯一存在的列(即没有使用聚合函数或计算列)

  • 视图中的行与基础表中的行之间必须能够一对一地映射

  • 视图中没有使用 GROUP BY 或 DISTINCT 子句

  • 视图中的列没有被嵌套查询引用

如果视图定义中包含了以下任何一项,那么它通常被认为是不可更新的:

  • 聚合函数(如 SUM、AVG、COUNT 等)

  • DISTINCT 关键字

  • GROUP BY 子句

  • 使用了 JOIN 操作的复杂查询(尽管在某些情况下,简单的 JOIN 视图也可能是可更新的,但这取决于具体的数据库实现和查询的复杂性)

  • 子查询在 SELECT 列表中(除非这些子查询不引用表中的任何行)

  • UNION 或 UNION ALL 操作符

  • 某些类型的表达式(如计算字段)

# 删除视图

drop view if exists `high_salaries`

# 查看视图

如果给你一个数据库,你怎么知道在这个数据库中是否有视图呢?

show full tables from `hrs` where `table_type` = 'VIEW';

怎么查看视图的结构呢?

desc `view_name`;


show create view `view_name`;

# 函数

在 mysql 中,函数分为内置函数(系统函数),用户自定义函数(UDF)

# 内置函数

  • 字符串函数:concat(),length(),substring(),replace()等,用于处理字符串函数

  • 数学函数:abs(),ceil(),floor(),round()等,用于执行数学运算

  • 日期和时间函数:now(),curdate(),date_format(),datediff()等,用于处理日期和时间数据

  • 聚合函数:sum(),avg(),count(),max(),min()等,用于对数据进行汇总

  • 条件函数:如if(),case语句等,用于在查询中实现条件逻辑

# 用户自定义函数

UDF 需要使用 C 或 C++ 编写,并编译成动态链接库(DLL 或 so 文件),然后在 MySQL 服务器上注册。

以下是自定义函数:

delimiter $$

create function fn_truncate_string(
    content varchar(10000),
    max_length int unsigned
) returns varchar(10000) no sql
begin
    declare result varchar(10000) default content;
    if char_length(content) > max_length then
        set result = left(content, max_length);
        set result = concat(result, '……');
    end if;
    return result;
end $$

delimiter ;

函数声明后面的no sql是声明函数体并没有使用 SQL 语句;如果函数体中需要通过 SQL 读取数据,需要声明为reads sql data。

定义函数前后的delimiter命令是为了修改终止符(定界符),因为函数体中的语句都是用;表示结束,如果不重新定义定界符,那么遇到的;的时候代码就会被截断执行,显然这不是我们想要的效果。

调用函数:

select fn_truncate_string('和我在成都的街头走一走,直到所有的灯都熄灭了也不停留', 10) as short_string;

# 存储过程

在MySQL中,存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,它存储在数据库中,可以通过指定的名称并传递参数(如果有的话)来被调用执行。存储过程可以被视为数据库中的一个程序,它封装了处理数据的逻辑,以便可以重复使用。

存储过程的特点:

  • 封装性:存储过程将复杂的SQL逻辑封装在一个单元中,简化了复杂操作的使用

  • 可重用性:一旦创建,存储过程就可以在多个地方被调用,无需重复编写相同的SQL代码

  • 安全性:通过限制对存储过程的访问权限,可以更安全地控制对数据库的操作

  • 减少网络流量:由于存储过程在数据库服务器上执行,因此可以减少客户端和服务器之间的数据传输量

  • 性能优化:数据库可以对存储过程进行优化,因为它们的执行计划可以在第一次执行时被缓存并重用

# 创建存储过程

CREATE PROCEDURE procedure_name (  
    [IN | OUT | INOUT] parameter_name datatype,  
    ...  
)  
BEGIN  
    -- SQL语句  
    -- 可以是DDL、DML、DCL语句  
    -- 可以使用变量、条件语句、循环等  
END;

举例1

DELIMITER //  
  
CREATE PROCEDURE AddNumbers(IN num1 INT, IN num2 INT, OUT result INT)  
BEGIN  
    SET result = num1 + num2;  
END //  
  
DELIMITER ;  
  
-- 调用存储过程  
CALL AddNumbers(5, 10, @sum);  
  
-- 查询输出参数的值  
SELECT @sum AS SumResult;

举例2

delimiter $$

create procedure sp_upgrade_salary()
begin
    declare flag boolean default 1;
    -- 定义一个异常处理器
    declare continue handler for sqlexception set flag=0;

    -- 开启事务环境
    start transaction;
    
    update tb_emp set sal=sal+300 where dno=10;
    update tb_emp set sal=sal+800 where dno=20;
    update tb_emp set sal=sal+500 where dno=30;

    -- 提交或回滚事务
    if flag then
        commit;
    else
        rollback;
    end if;
end $$

delimiter ;


call sp_upgrade_salary();

# UDF与存储过程的区别

  • 返回值:用户自定义函数必须有返回值,存储过程通常不直接返回值

  • 调用方式:存储过程使用 call 语句进行调用,而用户自定义函数可以在任何使用函数的地方调用,如select语句等