函数、存储过程和聚集(聚合)函数

一、基本概念

1. 函数 (Function)

定义: 完成特定功能、有返回值的数据库对象
特点:

必须有返回值 (单个值或表)

可以在 SQL 语句中直接调用

通常用于计算和转换数据

不能有副作用(不能修改数据库状态)

sql 复制代码
-- 示例:标量函数(返回单个值)
CREATE FUNCTION dbo.GetEmployeeName(@EmpID INT)
RETURNS VARCHAR(100)
AS
BEGIN
    DECLARE @Name VARCHAR(100);
    SELECT @Name = Name FROM Employees WHERE EmpID = @EmpID;
    RETURN @Name;
END;

-- 使用
SELECT dbo.GetEmployeeName(1001);

-- 示例:表值函数(返回表)
CREATE FUNCTION dbo.GetEmployeesByDept(@DeptID INT)
RETURNS TABLE
AS
RETURN (
    SELECT * FROM Employees WHERE DeptID = @DeptID
);

2. 存储过程 (Stored Procedure)

定义:预编译的 SQL 代码块 ,可包含复杂的业务逻辑
特点:

可以没有返回值 ,也可以有多个返回值 (通过 OUTPUT 参数)

不能在 SQL 语句中直接调用(使用 EXEC 执行)

可以修改数据库状态 (INSERT、UPDATE、DELETE)

支持复杂的事务处理和错误处理

性能更好(预编译)

sql 复制代码
-- 示例:存储过程
CREATE PROCEDURE sp_UpdateEmployeeSalary
    @EmpID INT,
    @IncreasePercent DECIMAL(5,2),
    @NewSalary DECIMAL(10,2) OUTPUT
AS
BEGIN
    BEGIN TRANSACTION;
    TRY
        UPDATE Employees 
        SET Salary = Salary * (1 + @IncreasePercent/100)
        WHERE EmpID = @EmpID;
        
        SELECT @NewSalary = Salary FROM Employees WHERE EmpID = @EmpID;
        
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION;
        THROW;
    END CATCH;
END;

-- 使用
DECLARE @NewSal DECIMAL(10,2);
EXEC sp_UpdateEmployeeSalary 1001, 10, @NewSal OUTPUT;

3. 聚集/聚合函数 (Aggregate Function)

定义 :对一组值进行计算返回单个值 的函数
特点

内置函数 (SUM、AVG、COUNT、MAX、MIN 等)

通常与 GROUP BY 一起使用

多行数据进行聚合计算

返回单个汇总值

sql 复制代码
-- 示例:聚合函数
SELECT 
    DeptID,
    COUNT(*) AS EmployeeCount,      -- 计数
    AVG(Salary) AS AvgSalary,       -- 平均
    SUM(Salary) AS TotalSalary,     -- 求和
    MAX(Salary) AS MaxSalary,       -- 最大
    MIN(Salary) AS MinSalary        -- 最小
FROM Employees
GROUP BY DeptID;

二、主要区别

三、联系与共同点

1. 都是数据库对象

都存储在数据库中

都可以重复使用

都能提高代码复用性

2. 都能封装逻辑

sql 复制代码
-- 函数封装计算逻辑
CREATE FUNCTION dbo.CalculateTax(@Amount DECIMAL)
RETURNS DECIMAL
AS BEGIN
    RETURN @Amount * 0.13;
END;

-- 存储过程封装业务流程
CREATE PROCEDURE sp_ProcessOrder
    @OrderID INT
AS
BEGIN
    -- 检查库存
    -- 更新订单状态
    -- 扣减库存
    -- 记录日志
END;

-- 聚合函数封装统计逻辑
SELECT AVG(Salary) FROM Employees; -- 封装了求平均的逻辑

3. 都可以带参数

sql 复制代码
-- 函数参数
SELECT dbo.GetEmployeeName(1001);

-- 存储过程参数
EXEC sp_UpdateSalary 1001, 10;

-- 聚合函数(隐式参数)
SELECT AVG(Salary) -- Salary 是隐式参数

4. 可以组合使用

sql 复制代码
-- 在存储过程中使用函数和聚合函数
CREATE PROCEDURE sp_DepartmentStats
    @DeptID INT
AS
BEGIN
    DECLARE @AvgSalary DECIMAL;
    
    -- 使用聚合函数
    SELECT @AvgSalary = AVG(Salary) 
    FROM Employees 
    WHERE DeptID = @DeptID;
    
    -- 使用自定义函数
    SELECT 
        EmpID,
        Name,
        dbo.CalculateTax(Salary) AS Tax
    FROM Employees
    WHERE DeptID = @DeptID;
END;

四、使用场景建议

✅ 使用函数的场景
需要在查询中计算或转换数据
需要返回单个值或表
逻辑相对简单,无副作用
✅ 使用存储过程的场景
复杂的业务流程
需要事务控制
需要批量处理数据
需要错误处理
多个相关操作需要一起执行
✅ 使用聚合函数的场景
统计分析
报表生成
数据汇总
GROUP BY 分组计算

五、实际例子对比

sql 复制代码
-- 1. 函数:计算员工年龄
CREATE FUNCTION dbo.CalculateAge(@BirthDate DATE)
RETURNS INT
AS
BEGIN
    RETURN DATEDIFF(YEAR, @BirthDate, GETDATE());
END;

-- 2. 存储过程:处理员工晋升
CREATE PROCEDURE sp_PromoteEmployee
    @EmpID INT,
    @NewPosition VARCHAR(50)
AS
BEGIN
    BEGIN TRANSACTION;
    
    UPDATE Employees 
    SET Position = @NewPosition,
        Salary = Salary * 1.2,
        PromoteDate = GETDATE()
    WHERE EmpID = @EmpID;
    
    INSERT INTO PromotionHistory (EmpID, NewPosition, PromoteDate)
    VALUES (@EmpID, @NewPosition, GETDATE());
    
    COMMIT TRANSACTION;
END;

-- 3. 聚合函数:统计部门信息
SELECT 
    d.DeptName,
    COUNT(e.EmpID) AS EmpCount,           -- 聚合:计数
    AVG(dbo.CalculateAge(e.BirthDate)) AS AvgAge,  -- 聚合 + 函数
    SUM(e.Salary) AS TotalBudget          -- 聚合:求和
FROM Departments d
LEFT JOIN Employees e ON d.DeptID = e.DeptID
GROUP BY d.DeptName;
相关推荐
麦聪聊数据5 小时前
企业数据流通与敏捷API交付实战(五):异构数据跨库联邦与零代码发布
数据库·sql·低代码·restful
Elastic 中国社区官方博客5 小时前
当 TSDS 遇到 ILM:设计不会拒绝延迟数据的时间序列数据流
大数据·运维·数据库·elasticsearch·搜索引擎·logstash
Omics Pro5 小时前
虚拟细胞:开启HIV/AIDS治疗新纪元的关键?
大数据·数据库·人工智能·深度学习·算法·机器学习·计算机视觉
J2虾虾5 小时前
MySQL的基本操作
数据库·mysql
arvin_xiaoting5 小时前
OpenClaw学习总结_III_自动化系统_3:CronJobs详解
数据库·学习·自动化
杨云龙UP5 小时前
Oracle 中 NOMOUNT、MOUNT、OPEN 怎么理解? 在不同场景下如何操作?_20260402
linux·运维·数据库·oracle
jzwugang6 小时前
postgresql链接详解
数据库·postgresql
2601_949815336 小时前
MySQL输入密码后闪退?
数据库·mysql·adb
lifewange6 小时前
Redis的测试要点和测试方法
数据库·redis·缓存
_下雨天.6 小时前
MySQL高可用
数据库·mysql