SQL Server 自定义函数、存储过程与触发器实战详解

七月
七月
发布时间:2026-10-14| 6人浏览过
原创

在 SQL Server 开发中,自定义函数、存储过程和触发器是提升数据处理效率与管理规范性的核心工具。本文涵盖标量函数、内联表值函数、多语句表值函数的语法差异与实现,详细演示 Cursor 游标的读取与更新逻辑,以及存储过程参数传递和触发器对表变更的拦截与记录方法。

一、自定义函数 (FUNCTION)

在 SQL Server 中,自定义函数主要分为三类:标量函数(Scalar)、内联表值函数(Inline Table-Valued)和多语句表值函数(Multi-Statement Table-Valued)。它们共同点是都使用 CREATE FUNCTION 定义名称、参数及返回值类型,但具体结构和调用方式存在显著区别。

第1步:创建并调用标量函数

标量函数返回单个数据类型的值。适用于需要基于输入参数返回单一结果的场景。

  • 目的:根据员工姓名查询工号。
  • 代码示例:
USE SQL_SYSTEM 
GO
CREATE FUNCTION F_GONGHAO(@XINGMING NVARCHAR(5))
RETURNS INT 
AS
BEGIN
DECLARE @GONGHAO INT
SET @GONGHAO =(SELECT Y.工号 FROM[T_员工信息] AS Y WHERE Y.姓名 =@XINGMING )
RETURN @GONGHAO
END
GO
/* 调用时注意模式前缀 dbo */
SELECT [姓名],DBO.F_GONGHAO ('李异峰')AS 工号 FROM[T_员工信息] WHERE [姓名]='李异峰'
GO

执行结果如下:

第2步:创建并调用内联表值函数

内联表值函数直接返回一个结果集(Table),语法上没有 BEGIN...END 块,而是直接用 RETURN (SELECT ...)。它类似于一个可带参数的视图。

  • 目的:筛选特定日期所有员工的打卡记录。
  • 代码示例:
USE SQL_SYSTEM 
GO
CREATE FUNCTION F_DAKAJILU(@RIQI DATE)
RETURNS TABLE
AS
RETURN(
SELECT* FROM[T_考勤]AS K WHERE K.日期 =@RIQI 
)
GO
/* 调用方式 */
SELECT*
FROM [F_DAKAJILU]('2014/02/28')
GO

执行结果如下:

第3步:创建多语句表值函数

多语句表值函数允许在函数体内执行多条 SQL 语句,并将结果插入到一个预定义的表变量中。它结合了标量函数的过程化和表值函数的结果集特性。

  • 目的:返回指定日期的打卡记录及对应的员工个人信息。
  • 关键点:必须先用 RETURNS @TABLE TABLE (...) 定义表变量的列结构。
USE SQL_SYSTEM 
GO
CREATE FUNCTION F_D_DAKAJILU(@RIQI DATE)
RETURNS @TEMP_TABLE TABLE( 
[姓名]NVARCHAR(5) NOT NULL,
[工号]INT NOT NULL,
[职位]NVARCHAR(10) NOT NULL,
[部门]NVARCHAR(5) NOT NULL,
[是否夜班]NCHAR(1) NOT NULL,
[日期]DATE NOT NULL,
[当天上班时间]FLOAT(1)NOT NULL
) 
AS
BEGIN
INSERT INTO @TEMP_TABLE 
SELECT Y.姓名 ,Y.工号 ,Y.职位 ,Y.部门 ,K.夜班 ,K.日期 ,K.当天上班时间 
FROM[T_员工信息] AS Y CROSS JOIN [T_考勤] AS K 
WHERE Y.工号 =K.工号 AND K.日期=@RIQI 
RETURN 
END
GO
SELECT*
FROM[F_D_DAKAJILU]('2014/02/28')
GO

执行结果如下:

二、Cursor 游标的使用

当需要对数据进行逐行处理(如复杂计算或逐条更新)时,可以使用游标。其基本生命周期包括:声明、打开、读取、关闭和释放。

第1步:声明并读取只读数据

  • 目的:遍历员工信息表,打印每条记录的姓名、工号等。
  • 关键步骤:使用 FETCH ... INTO 将数据存入局部变量,并通过 WHILE (@@FETCH_STATUS = 0) 控制循环。
USE SQL_SYSTEM 
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION CURSOR_READ_T_员工信息
DECLARE CUR_T_员工信息 SCROLL CURSOR FOR
SELECT Y.姓名 ,Y.工号 ,Y.职位 ,Y.部门 FROM[T_员工信息]AS Y ORDER BY Y.工号 ASC 
COMMIT TRANSACTION CURSOR_READ_T_员工信息
GO
/* 打开游标 */
OPEN GLOBAL CUR_T_员工信息
GO
/* 读取数据 */
DECLARE @XINGMING NVARCHAR(3),@GONGHAO INT,@ZHIWEI NVARCHAR(10),@BUMEN NVARCHAR(8)
PRINT'当前数据表中有'+CAST(@@CURSOR_ROWS AS NVARCHAR(6))+'行数据。'
FETCH FIRST FROM[CUR_T_员工信息]INTO @XINGMING,@GONGHAO,@ZHIWEI,@BUMEN
WHILE(@@FETCH_STATUS =0)
BEGIN
PRINT'姓名:'+@XINGMING+' 工号:'+CONVERT(VARCHAR(3),@GONGHAO)+' 职位:'+@ZHIWEI+' 部门:'+@BUMEN
FETCH NEXT FROM[CUR_T_员工信息] INTO @XINGMING,@GONGHAO,@ZHIWEI,@BUMEN
END
GO

执行输出如下:

第2步:利用游标进行数据更新

若需修改数据,声明游标时需使用 FOR UPDATE。在循环中使用 WHERE CURRENT OF cursor_name 定位当前行进行更新。

  • 风险提醒:批量更新操作前务必备份数据或确认测试环境,避免误改生产数据。
  • 代码示例:
USE SQL_SYSTEM 
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRANSACTION TR_UPDATE_YG 
DECLARE CUR_T_YG CURSOR FOR
SELECT YG.姓名 ,YG.工号 ,YG.职位 ,YG.部门 FROM[T_yuangongxinxi]AS YG FOR UPDATE 
OPEN GLOBAL CUR_T_YG
COMMIT TRANSACTION TR_UPDATE_YG
GO
/* 更新操作 */
PRINT'当前有'+CONVERT(VARCHAR(3),@@CURSOR_ROWS)+'条数据行。'
FETCH NEXT FROM[CUR_T_YG]
WHILE(@@FETCH_STATUS=0)
BEGIN
UPDATE[T_yuangongxinxi] 
SET[工号] =[工号]+100
WHERE CURRENT OF CUR_T_YG
FETCH NEXT FROM[CUR_T_YG] 
END
GO
/* 查看更新后的结果 */
SELECT * FROM [T_yuangongxinxi]

更新后数据验证如下:

注意:操作完成后,务必执行 CLOSE 和 DEALLOCATE 以释放资源。

三、存储过程 (PROCEDURE)

存储过程是预编译的 SQL 集合,支持参数传递,可用于封装复杂的业务逻辑。

第1步:无参数存储过程

  • 目的:查询某张表的所有数据,并演示如何将执行结果插入新表。
USE SQL_SYSTEM 
GO
CREATE PROCEDURE PRO_SELECT_T
WITH RECOMPILE 
AS
SELECT* FROM[T_yuangongxinxi] 
GO
/* 执行 */
EXECUTE PRO_SELECT_T
GO

执行结果如下:

可以将执行结果导入新表:INSERT INTO AA EXECUTE PRO_SELECT_T。

第2步:带输出参数的存储过程

  • 目的:修改员工职位,并返回修改前后的状态信息。
  • 关键点:参数定义时需标记为 OUTPUT,调用时也需指定 OUTPUT。
USE SQL_SYSTEM 
GO
CREATE PROCEDURE PRO_DAUP_ZHIWEI
@GONGHAO INT, @ZHIWEI NVARCHAR(10), @RETURNS NVARCHAR(50) OUTPUT
AS
BEGIN
DECLARE @QIAN_RETURN_ZHIWEI NVARCHAR(10),@XINGMING NVARCHAR(3)
SELECT @QIAN_RETURN_ZHIWEI =AA.职位 ,@XINGMING =AA.姓名 
FROM[AA] WHERE AA.工号 =@GONGHAO 
UPDATE[AA] SET[职位] =@ZHIWEI WHERE AA.工号 =@GONGHAO 
SET @RETURNS = '已经成功将工号为:【'+CONVERT(VARCHAR(3),@GONGHAO)+'】,姓名为:【'+@XINGMING+'】,职位【'+
@QIAN_RETURN_ZHIWEI+'】更新为:【'+@ZHIWEI
END
/* 调用示例 */
DECLARE @PRINTF NVARCHAR(50);
EXECUTE PRO_DAUP_ZHIWEI 101,'SQL工程师',@PRINTF OUTPUT 
SELECT @PRINTF AS '更新消息'

执行反馈结果如下:

四、触发器 (TRIGGER)

触发器是一种特殊的存储过程,它在特定的数据库事件(如 INSERT、UPDATE、DELETE)发生时自动执行。分为 DDL 触发器和 DML 触发器。

第1步:DDL 触发器保护表

  • 目的:防止用户删除指定的表。
USE SQL_SYSTEM 
GO
CREATE TRIGGER DROP_T
ON DATABASE FOR DROP_TABLE 
AS
BEGIN
ROLLBACK TRANSACTION 
PRINT'不能删除的,因为我已经添加了触发保护!'
END
GO
/* 尝试删除会失败 */
DROP TABLE AA

触发拦截效果如下:

第2步:DML 触发器审计数据更新

DML 触发器在处理数据变更时,可以访问两个虚拟表:inserted(保存新增或更新后的数据)和 deleted(保存旧数据或被删除的数据)。这对于数据审计非常有用。

  • 原理图示:

- Insert 操作:

- Delete 操作:

- Update 操作:

第3步:编写 Update 触发器

  • 目的:在更新员工职位时,自动将更新前和更新后的数据分别保存到历史记录表中。
  • 代码逻辑:
USE [SQL_SYSTEM]
GO
CREATE TRIGGER [dbo].[UPDATE_T]
ON [dbo].[AA] INSTEAD OF UPDATE
AS
BEGIN
/* 将新数据存入历史表 */
INSERT INTO T_UPDATE_HOU SELECT* FROM[inserted] 
/* 将旧数据存入历史表 */
INSERT INTO T_UPDATE_QIAN SELECT* FROM[deleted]
PRINT'更新完毕,更新前的数据已经写入到【T_UPDATE_QIAN】,更新后的数据插入到【T_UPDATE_HOU】。'
END
GO
/* 触发更新 */
UPDATE[AA] SET AA.职位 ='SQL高级工程师' WHERE AA.工号=101
GO
/* 检查结果 */
SELECT* FROM[T_UPDATE_HOU] -- 显示更新后的数据
SELECT* FROM[T_UPDATE_QIAN] -- 显示更新前的数据

查看更新后的结果表:

查看更新前的结果表:

以上就是 SQL Server 自定义函数、存储过程与触发器应用的详细内容,更多关于数据库开发优化的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。