当前位置: 首页 > news >正文

视图、存储过程与函数

前言:为什么需要这三样东西?

想象一下,你是一家大型超市的经理:

  • 表(Table) 就像你的货架——上面摆着真实的商品(数据)。
  • 但顾客不需要知道所有货架怎么摆放,他们只想“看热销商品”或“查某类折扣品”;
  • 收银员也不用每次都手动算总价,而是调用一个“计算账单”的流程;
  • 财务人员更不会直接翻货架,而是用一个“计算税费”的公式。

在数据库中:

  • 视图 就是给顾客看的“精选商品清单”;
  • 存储过程 就是收银员执行的“结账流程”;
  • 函数 就是财务用的“税率计算器”。

它们都不是真实的数据容器,而是对数据操作的封装与抽象,目的是让系统更安全、更高效、更易维护。


一、视图(View):虚拟的“数据窗口”

1.1 标准定义(ISO/IEC 9075)

视图(View) 是一个命名的、持久化的查询表达式,其结果表现为一张虚拟表(Virtual Table)。视图本身不存储数据(除非是物化视图),其内容在每次被引用时动态计算自一个或多个基表(Base Tables)。

形式化语法(简化自 SQL/Foundation):

CREATE VIEW view_name [ ( column_list ) ]
AS query_expression
[ WITH [ CASCADED | LOCAL ] CHECK OPTION ];

1.2 通俗解释

你可以把视图理解为:

“一张永远最新的动态报表”

它不像普通表那样存数据,而是记住了一个查询语句。每次你“打开这张报表”,数据库就立刻执行这个查询,把最新结果呈现给你。

举个例子
你有一张员工表 employees 和部门表 departments。你想经常查看“销售部的在职员工”。
与其每次都写:

SELECT e.name, e.salary, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.id
WHERE d.dept_name = 'Sales' AND e.status = 'active';

不如创建一个视图:

CREATE VIEW sales_team AS
SELECT e.name, e.salary
FROM employees e
JOIN departments d ON e.dept_id = d.id
WHERE d.dept_name = 'Sales' AND e.status = 'active';

之后只需:

SELECT * FROM sales_team;

——就像打开一个固定的“销售团队名单”,但里面的数据永远是最新的!

1.3 关键特性详解

特性技术说明通俗类比
虚拟性不占用额外存储空间(除临时结果外)报表不是复印的,而是实时打印的
只读倾向默认用于查询;更新需满足严格条件你能看名单,但不能直接在名单上改名字(除非系统允许)
封装复杂性隐藏多表 JOIN、子查询等逻辑顾客不用知道仓库在哪,只看前台展示
安全控制可授权用户访问视图而非基表实习生只能看“公开客户列表”,看不到完整客户表
逻辑独立性基表结构变化时,若视图仍有效,应用无需改超市换货架布局,但“热销区”指示牌不变

1.4 可更新视图(Updatable View)

SQL 标准规定,只有当视图满足以下条件时,才允许通过它修改底层数据:

  • 查询仅基于单个基表
  • 不包含 DISTINCTGROUP BYHAVING、聚合函数(如 SUM());
  • SELECT 列表中不含表达式(如 salary * 1.1)或常量;
  • 包含基表的主键或唯一非空约束列。

可更新示例

CREATE VIEW active_employees AS
SELECT id, name, salary FROM employees WHERE status = 'active';
-- 可执行:UPDATE active_employees SET salary = 8000 WHERE id = 101;

不可更新示例

CREATE VIEW dept_avg_salary AS
SELECT dept_id, AVG(salary) AS avg_sal FROM employees GROUP BY dept_id;
-- 无法更新:平均工资不是某个具体员工的属性!

1.5 物化视图(Materialized View)——高级扩展

⚠️ 注意:物化视图不属于 SQL 标准核心,是各厂商扩展功能。

  • 普通视图:每次查都重新算(实时但慢);
  • 物化视图:把结果物理存下来,定期刷新(快但非实时)。
数据库是否支持物化视图
Oracle✅(CREATE MATERIALIZED VIEW
PostgreSQL✅(需手动 REFRESH
SQL Server✅(称为“索引视图”)
MySQL❌(需用定时任务+临时表模拟)

💡 通俗理解
普通视图 = 每次现场做菜;
物化视图 = 提前做好菜放冰箱,吃的时候热一下。

1.6 使用场景总结

强烈推荐使用视图的场景

  • 简化复杂查询(尤其涉及多表关联);
  • 实现行级/列级数据安全(最小权限原则);
  • 提供向后兼容接口(表结构变更时保护老应用);
  • 构建业务语义层(如 high_value_customers, pending_orders)。

应避免使用视图的场景

  • 需要传入运行时参数(标准 SQL 视图不支持参数);
  • 追求极致性能且数据量极大(考虑物化视图或应用缓存);
  • 深度嵌套(如视图 A → 视图 B → 视图 C),导致优化器失效。

二、存储过程(Stored Procedure):数据库中的“自动化脚本”

2.1 标准定义(ISO/IEC 9075-4: SQL/PSM)

存储过程(Stored Procedure) 是一个命名的、持久化的程序模块,由一组 SQL 语句和过程化控制结构(如变量声明、条件判断、循环、异常处理)组成,可接受输入(IN)、输出(OUT)或双向(INOUT)参数,并通过 CALL 语句调用执行。其主要用途是封装复杂的业务逻辑和数据操作流程。

形式化语法(简化):

CREATE PROCEDURE proc_name ( [ parameter_declaration [, ...] ] )[ characteristic ... ]routine_body

2.2 通俗解释

你可以把存储过程理解为:

“数据库里的自动化机器人”

你告诉它:“当我调用你时,请按顺序做这几件事:先检查库存,再扣减数量,然后记录日志,最后发通知。”
它就会忠实地执行这一整套流程,并且可以在过程中“记住中间结果”(变量)、“根据情况做不同事”(IF/ELSE)、“重复做事”(LOOP)。

举个例子:银行转账
你不想让应用层分别执行“扣款”和“入账”两个操作(万一中间断了怎么办?),于是写一个存储过程:

CREATE PROCEDURE Transfer(IN from_account INT,IN to_account INT,IN amount DECIMAL(12,2),OUT result_message VARCHAR(100)
)
BEGINDECLARE EXIT HANDLER FOR SQLEXCEPTIONBEGINROLLBACK;SET result_message = '转账失败:系统异常';END;START TRANSACTION;UPDATE accounts SET balance = balance - amount WHERE id = from_account;UPDATE accounts SET balance = balance + amount WHERE id = to_account;INSERT INTO transaction_log VALUES (..., 'SUCCESS');COMMIT;SET result_message = '转账成功';
END;

应用只需调用:

CALL Transfer(1001, 1002, 500.00, @msg);
SELECT @msg;

——整个流程原子执行,要么全成功,要么全回滚。

2.3 核心特性详解

特性技术说明通俗类比
过程化逻辑支持变量、IF、WHILE、异常处理等机器人能“思考”和“决策”
DML/DDL 支持可执行 INSERT/UPDATE/DELETE/CREATE 等能实际“动手操作货架”
事务控制可包含 COMMIT/ROLLBACK(行为因 DBMS 而异)能保证“一套动作要么全做完,要么全不算”
参数模式支持 IN(输入)、OUT(输出)、INOUT(双向)能接收指令,也能返回结果
无直接返回值不能像函数那样“返回一个值”,但可通过 OUT 参数传回机器人不会“吐出一个数字”,但会“写一张纸条给你”
调用方式必须通过 CALLEXECUTE 单独调用你需要明确“启动机器人”,不能把它塞进一句话里

2.4 事务语义差异(重要!)

不同数据库对存储过程中事务的处理不同,这是工程实践中极易踩坑的点:

数据库默认事务行为注意事项
Oracle过程内 COMMIT 会提交整个事务若需局部提交,需使用“自治事务(Autonomous Transaction)”
SQL Server支持嵌套事务,COMMIT 仅减少计数器需配合 @@TRANCOUNT 使用
PostgreSQLv11+ 支持 PROCEDURE 中的 COMMIT/ROLLBACK旧版本只能用函数(不能控制事务)
MySQL默认自动提交,需显式 START TRANSACTION过程内可控制事务,但需谨慎

最佳实践事务边界尽量在应用层控制,存储过程内避免 COMMIT,除非有强一致性要求。

2.5 使用场景总结

典型适用场景

  • 执行多步骤、强一致性的业务流程(如订单创建、支付、库存扣减);
  • 批量数据处理(ETL、数据归档、清洗);
  • 封装敏感操作(如删除用户数据),仅暴露过程接口;
  • 减少网络往返(一次调用执行多条 SQL,提升性能);
  • 实现数据库端的定时任务(配合调度器)。

应谨慎使用的场景

  • 简单 CRUD 操作(过度封装反而增加复杂度);
  • 核心业务逻辑(难以测试、调试、版本控制);
  • 跨数据库迁移项目(存储过程语法差异极大)。

三、函数(Function):SQL 表达式中的“计算器”

3.1 标准定义(ISO/IEC 9075-4: SQL/PSM)

函数(Function) 是一个必须返回单一值(标量值或表)的命名程序单元,可在 SQL 表达式中直接调用(如 SELECTWHEREORDER BY 子句)。函数的设计初衷是封装无副作用的计算逻辑,强调确定性可复用性

SQL 标准将函数分为两类:

  • 标量函数(Scalar Function):返回单个值(如 UPPER('abc') → 'ABC');
  • 表值函数(Table-Valued Function, TVF):返回结果集(如 Oracle 的 pipelined function)。

形式化语法(标量函数):

CREATE FUNCTION func_name ( param type [, ...] )
RETURNS return_type
[ DETERMINISTIC | NOT DETERMINISTIC ]
[ READS SQL DATA | MODIFIES SQL DATA | NO SQL ]
routine_body

3.2 通俗解释

你可以把函数理解为:

“一个插在 SQL 语句里的小计算器”

它不能自己“做事”,但能在你写查询的时候,“当场帮你算一个值”。

举个例子:计算个人所得税
你不想在应用里写税率逻辑,也不想每次手动算,于是创建一个函数:

CREATE FUNCTION calc_tax(income NUMERIC)
RETURNS NUMERIC
DETERMINISTIC
READS SQL DATA
BEGINIF income <= 5000 THEN RETURN 0;ELSEIF income <= 10000 THEN RETURN (income - 5000) * 0.1;ELSE RETURN 500 + (income - 10000) * 0.2;END IF;
END;

然后在查询中直接使用:

SELECT name, salary, calc_tax(salary) AS tax FROM employees;

——就像在 Excel 里写 =TAX(A2) 一样自然!

3.3 副作用与 DML 限制(关键!)

SQL 标准不禁止函数包含 DML(如 INSERT),但强烈反对,因为:

  • 函数被设计用于表达式上下文(如 SELECT),而 SELECT 应是只读操作
  • 如果函数偷偷改了数据,会导致查询结果不可预测,破坏 ACID 语义。

各数据库的实际策略

数据库允许函数含 DML?能否在 SELECT 中调用含 DML 的函数?
Oracle✅ 允许❌ 禁止(报错 ORA-14551)
SQL Server✅ 允许⚠️ 在某些上下文(如计算列)中禁止
PostgreSQL✅ 允许✅ 允许(但需声明 VOLATILE
MySQL❌ 严格禁止

黄金法则函数应是“纯函数”——相同输入永远返回相同输出,且不修改任何外部状态

3.4 表值函数(TVF):带参数的“动态视图”

由于标准视图不支持参数表值函数(TVF) 成为实现“参数化查询”的标准方案。

示例(SQL Server)

CREATE FUNCTION GetEmployeesByDept(@dept_id INT)
RETURNS TABLE
AS
RETURN (SELECT id, name, salaryFROM employeesWHERE dept_id = @dept_id
);

使用:

SELECT * FROM GetEmployeesByDept(10);

💡 通俗理解
视图 = 固定菜单;
表值函数 = “告诉我你想吃什么部门,我给你列出来”。

数据库是否支持 TVF
Oracle✅(Pipelined Functions)
SQL Server✅(内联 TVF 性能极佳)
PostgreSQL✅(RETURNS TABLE (...)
MySQL❌(仅支持标量函数)

3.5 使用场景总结

强烈推荐使用函数的场景

  • 字段格式化(如 format_phone(phone));
  • 数值计算(税率、折扣、年龄、评分);
  • 数据验证(如 is_valid_email(email));
  • 构建生成列(Generated Columns);
  • WHERE 中复用复杂条件(如 WHERE is_weekend(order_date))。

应避免的场景

  • 在函数中执行 DML 或事务操作;
  • 实现复杂业务流程(应交给存储过程或应用层);
  • 依赖会话变量或非确定性操作(除非明确声明 NOT DETERMINISTIC)。

四、三者终极对比:一张表看懂本质区别

维度视图(View)存储过程(Stored Procedure)函数(Function)
本质虚拟表(结果集)自动化脚本(执行单元)计算器(表达式组件)
返回什么多行多列的表无直接返回值(可通过 OUT 参数返回多个值)一个值(标量)或一张表(TVF)
怎么调用SELECT * FROM view_nameCALL proc_name(...)SELECT func(col) FROM t
能改数据吗❌(定义中仅 SELECT)⚠️ 技术可能,但应禁止
能控制事务吗
支持参数吗❌(标准 SQL 不支持)✅(IN/OUT/INOUT)✅(通常仅 IN)
能在 SQL 表达式中用吗✅(作为表源)✅(作为值)
主要用途简化查询、安全控制执行流程、批处理、事务封装计算、字段转换
副作用允许应无(纯函数)
可移植性高(标准支持好)低(语法差异大)中(TVF 支持不一)

五、如何选择?—— 决策树

你想做什么?
│
├─ 想把一个复杂查询当作一张表来用? ──→ 视图(View)
│
├─ 需要传参数的动态查询结果? 
│   ├─ 数据库支持 TVF? ──→ 表值函数(TVF)
│   └─ 不支持? ──→ 存储过程 + 临时表 / 应用层拼接
│
├─ 想在 SELECT/WHERE 中计算一个值? ──→ 标量函数(Function)
│
├─ 要执行一系列 DML 并保证原子性? ──→ 存储过程(Procedure)
│
├─ 要实现行/列级安全? ──→ 视图 + 权限控制
│
└─ 要高性能缓存结果? ├─ 支持物化视图? ──→ 物化视图└─ 否? ──→ 应用层缓存

六、最佳实践与反模式

✅ 推荐实践

  1. 优先使用视图:它是标准、安全、可移植的数据抽象方式;
  2. 函数保持纯净:无 DML、无会话依赖、声明 DETERMINISTIC(若适用);
  3. 过程用于“动作”:只封装必须在数据库端完成的事务性操作;
  4. 避免逻辑下沉:核心业务规则放在应用层,便于测试与迭代;
  5. 文档化与版本控制:将数据库对象纳入 Git + CI/CD 流程。

❌ 经典反模式

反模式风险
在函数中写 INSERT/UPDATE导致查询产生副作用,破坏数据一致性
用存储过程实现整个业务逻辑代码难以测试、调试、重构,形成“数据库黑盒”
深度嵌套视图(>3层)优化器无法有效推导执行计划,性能急剧下降
依赖 MySQL 特有语法写过程未来迁移到 PostgreSQL/Oracle 时成本极高
视图中使用 SELECT *基表新增列后可能导致应用崩溃
http://www.dtcms.com/a/590395.html

相关文章:

  • JavaSE语法巩固——图书管理系统
  • Java 抽象类与接口深度解析:从概念到实战应用
  • 软件测试——自动化测试常用函数(超详细)
  • 韶关企业网站建设好看的seo网站
  • 网站网页区别是什么产品展示类网站源码
  • 网站规划的步骤微信开发公众平台
  • 怎么自己做直播网站吗制作公司网站一般多久能好
  • 公司合法网站域名怎么注册水冷眸WordPress
  • 网站开发应财务如何记账wordpress 字符替换插件
  • 网站建设运营成本什么是网络营销评估主要评估哪些方面
  • 升级 macOS 26.1 之后 MagicMouse2 疯狂卡顿飘移,已解决
  • 无锡高端网站建设舆情信息网站
  • 网站建设的成功之处有哪些怎么做一个自己公司的网页
  • 怎么建设自己产品网站加强红色网站建设
  • 【QT开发】天气预报项目(详细注释版)
  • Vue嵌套(多级)路由
  • 精品网站建设公司互联网产品推广是做什么的
  • 东莞360推广的网站是谁做的云南楚雄医药高等专科学校
  • 学做网站的视频建设银行电商网站
  • 目前网站类型主要包括哪几种江苏营销型网站建设公司
  • 临沂网站开发多少钱wordpress无法发送
  • 信用网站标准化建设模块都有哪些东道设计招聘要求
  • YOLO项目笔记
  • 网站制作合同模板汽车网站flash模板
  • 凡科网做网站能达到什么效果济南网站建设(力推聚搜网络)
  • 网站备案表格样本一个人做的网站做什么好
  • 做个网站好还是做淘宝好品牌推广网站策划设计
  • 启迪网站建设招聘成都景观设计公司有哪些
  • 12306网站建设费用怎么做app下载网站
  • 外设模块学习(18)——5针双轴按键摇杆模块(STM32)