带输出参数的存储过程非常有用,因为它允许存储过程在执行后,将一个或多个值“返回”给调用者(另一个存储过程、应用程序代码等),这与 SELECT 语句返回结果集不同,输出参数返回的是一个标量值(单个值)。

(图片来源网络,侵删)
核心概念
@parameter_name data_type: 这是输入参数,调用时必须传入值。@parameter_name data_type OUTPUT: 这是输出参数。OUTPUT关键字是必须的,它告诉 SQL Server 这个参数不仅用于输入,还要用于将值传出。@parameter_name data_type = default OUTPUT: 这是具有默认值的输出参数。
创建带输出参数的存储过程
我们将创建一个简单的存储过程,它接收一个员工的 EmployeeID,然后返回该员工的 LastName(作为输出参数)和 JobTitle(通过 SELECT 语句返回结果集)。
示例场景:
- 输入参数:
@EmployeeID(整数) - 输出参数:
@EmployeeLastName(字符串, nvarchar) - 结果集: 包含
JobTitle的单行结果
USE AdventureWorks2025; -- 使用一个示例数据库,如果没有,可以用你自己的数据库
GO
-- 创建存储过程
CREATE PROCEDURE dbo.usp_GetEmployeeDetailsByID
-- 输入参数
@EmployeeID INT,
-- 输出参数
@EmployeeLastName NVARCHAR(50) OUTPUT
AS
BEGIN
-- 声明一个变量来检查是否找到了员工
DECLARE @EmployeeCount INT;
-- 检查员工ID是否存在
SELECT
@EmployeeCount = COUNT(*),
@EmployeeLastName = LastName -- 将查询到的LastName赋值给输出参数
FROM
HumanResources.vEmployee -- 使用视图作为示例
WHERE
BusinessEntityID = @EmployeeID;
-- 根据查询结果返回不同的信息
IF @EmployeeCount > 0
BEGIN
-- 如果找到员工,通过SELECT语句返回其职位信息
-- 这是一个结果集,与输出参数是两种不同的返回方式
SELECT
JobTitle
FROM
HumanResources.vEmployee
WHERE
BusinessEntityID = @EmployeeID;
-- 可以在这里设置一个返回值(可选)
RETURN 0; -- 0 通常表示成功
END
ELSE
BEGIN
-- 如果没有找到员工,可以返回一个空的结果集和NULL作为输出参数
SELECT @EmployeeLastName = NULL;
-- 也可以返回一个自定义的消息(可选)
-- PRINT '员工未找到。';
-- 返回一个非零值表示失败(可选)
RETURN -1; -- -1 通常表示失败
END
END
GO
代码解释:
CREATE PROCEDURE dbo.usp_GetEmployeeDetailsByID: 定义存储过程名称和所有者。@EmployeeID INT: 一个输入参数,用于接收要查询的员工ID。@EmployeeLastName NVARCHAR(50) OUTPUT: 一个输出参数,用于存储查询到的员工姓氏。SELECT @EmployeeLastName = LastName ...: 这是关键一步,在查询时,我们将LastName列的值直接赋给了输出参数@EmployeeLastName。IF @EmployeeCount > 0: 我们检查是否存在该员工,以决定后续操作。SELECT JobTitle ...: 这行代码会向客户端返回一个结果集,一个存储过程可以同时返回一个结果集和一个或多个输出参数。RETURN 0/RETURN -1: 这是存储过程的返回值(状态码),与输出参数不同,它是一个整数,通常用于表示成功或失败。
执行带输出参数的存储过程
执行存储过程时,必须为输出参数准备一个变量来接收返回的值,并且同样需要使用 OUTPUT 关键字。

(图片来源网络,侵删)
示例 1: 成功执行
假设我们要查询 EmployeeID 为 1 的员工信息。
-- 1. 声明一个变量来接收输出参数的值
DECLARE @LastName NVARCHAR(50);
-- 2. 声明一个变量来接收存储过程的返回值(状态码)
DECLARE @ReturnStatus INT;
-- 3. 执行存储过程,传入输入参数,并指定输出参数变量
EXEC @ReturnStatus = dbo.usp_GetEmployeeDetailsByID
@EmployeeID = 1,
@EmployeeLastName = @LastName OUTPUT;
-- 4. 查看输出参数的值
SELECT
'存储过程返回值 (状态码)' AS StatusMessage,
@ReturnStatus AS ReturnValue;
SELECT
'从输出参数获取的员工姓氏' AS Message,
@LastName AS EmployeeLastName;
执行结果分析:
- 第一个
SELECT语句会显示:StatusMessage ReturnValue ------------------ ----------- 存储过程返回值 (状态码) 0 - 第二个
SELECT语句会显示:Message EmployeeLastName -------------------------------- -------------------- 从输出参数获取的员工姓氏 Wang - 由于存储过程内部执行了
SELECT JobTitle,你还会看到一个包含JobTitle的结果集。
示例 2: 执行失败(员工不存在)
-- 1. 声明变量
DECLARE @LastName NVARCHAR(50);
DECLARE @ReturnStatus INT;
-- 2. 执行一个不存在的员工ID
EXEC @ReturnStatus = dbo.usp_GetEmployeeDetailsByID
@EmployeeID = 999, -- 假设这个ID不存在
@EmployeeLastName = @LastName OUTPUT;
-- 3. 查看结果
SELECT
'存储过程返回值 (状态码)' AS StatusMessage,
@ReturnStatus AS ReturnValue;
SELECT
'从输出参数获取的员工姓氏' AS Message,
@LastName AS EmployeeLastName;
执行结果分析:

(图片来源网络,侵删)
- 第一个
SELECT语句会显示:StatusMessage ReturnValue ------------------ ----------- 存储过程返回值 (状态码) -1 - 第二个
SELECT语句会显示:Message EmployeeLastName -------------------------------- -------------------- 从输出参数获取的员工姓氏 NULL - 由于
IF条件不满足,存储过程不会返回包含JobTitle的结果集。
总结与最佳实践
| 特性 | SELECT 语句 |
OUTPUT 参数 |
RETURN 语句 |
|---|---|---|---|
| 目的 | 返回一个或多个完整的行(结果集) | 返回一个或多个标量值(单个值) | 返回一个整数的状态码(成功/失败) |
| 数据类型 | 表格结构 | 任何有效的 SQL Server 数据类型 | 整数 (int) |
| 调用方处理 | 应用程序代码(如 C#, Java)通常使用 DataReader 或 DataSet 来读取结果集。 |
必须在调用时声明一个变量并用 OUTPUT 关键字来接收。 |
通过 或 @RETURN_VALUE 等方式获取。 |
| 使用场景 | 当需要返回多个列、多行数据时,例如获取用户的完整订单列表。 | 当只需要返回一个或几个特定的值时,例如获取新插入记录的ID、计算的总数等。 | 用于表示操作的成功或失败,简单的状态指示。 |
最佳实践:
- 明确命名: 给存储过程和参数起一个清晰、有意义的名字。
usp_...表示用户存储过程,@..._ID表示ID参数,@..._OUTPUT或@..._Result表示输出参数。 - 处理 NULL 值: 调用方必须考虑到输出参数可能返回
NULL,并做好相应的处理逻辑,避免后续操作出错。 - 文档化: 为存储过程添加注释,说明其功能、参数含义(特别是输入/输出)以及返回值(状态码)的含义。
- 组合使用: 一个强大的存储过程可以同时使用
SELECT(返回结果集)、OUTPUT参数(返回关键标量值)和RETURN(返回状态码),为调用方提供最丰富的信息。
