在 SQL Server 中,存储过程是可接收参数的可复用程序,不返回值(如函数那样),但可返回执行状态。
第50.1节 创建并执行基本存储过程
使用 Library 数据库的 Authors 表:
sql
CREATE PROCEDURE GetName
@input_id INT = NULL, -- 输入参数:人员 ID,默认 NULL
@name VARCHAR(128) = NULL -- 输入参数:姓名,默认 NULL
AS
BEGIN
SELECT Name + ' is from ' + Country
FROM Authors
WHERE Id = @input_id OR Name = @name;
END
GO
执行方式:
- 使用 EXECUTE / EXEC
sql
EXECUTE GetName @input_id = 1;
EXEC GetName @name = 'Ernest Hemingway';
- 省略 EXEC,直接调用
sql
GetName NULL, 'Ernest Hemingway';
- 参数顺序可任意,只要显式指定:
sql
CREATE PROC dbo.sProcTemp
@Param1 INT,
@Param2 INT
AS
SELECT Param1 = @Param1, Param2 = @Param2;
-- 调用
EXEC dbo.sProcTemp @Param2 = 0, @Param1 = 1;
- 以
sp_开头并放在 master 数据库中的存储过程可在任意数据库中直接调用(优先搜索 master)。
第50.2节 带 IF...ELSE 与 INSERT 的存储过程
示例表:
sql
CREATE TABLE Employee (
Id INT,
EmpName VARCHAR(25),
EmpGender VARCHAR(6),
EmpDeptId INT
);
存储过程:检查参数非空后插入。
sql
CREATE PROCEDURE spSetEmployeeDetails
@ID INT,
@Name VARCHAR(25),
@Gender VARCHAR(6),
@DeptId INT
AS
BEGIN
IF (
@ID IS NOT NULL AND LEN(@ID) != 0
AND @Name IS NOT NULL AND LEN(@Name) != 0
AND @Gender IS NOT NULL AND LEN(@Gender) != 0
AND @DeptId IS NOT NULL AND LEN(@DeptId) != 0
)
BEGIN
INSERT INTO Employee (Id, EmpName, EmpGender, EmpDeptId)
VALUES (@ID, @Name, @Gender, @DeptId);
END
ELSE
PRINT 'Incorrect Parameters';
END
GO
调用:
sql
DECLARE @ID INT, @Name VARCHAR(25), @Gender VARCHAR(6), @DeptId INT;
EXEC spSetEmployeeDetails
@ID = 1,
@Name = 'Subin Nepal',
@Gender = 'Male',
@DeptId = 182666;
第50.3节 存储过程中的动态 SQL
当表名、列名或过滤值需在运行时决定,可拼接并执行动态 SQL。
sql
CREATE PROC sp_dynamicSQL
@table_name NVARCHAR(20),
@col_name NVARCHAR(20),
@col_value NVARCHAR(20)
AS
BEGIN
DECLARE @Query NVARCHAR(MAX);
SET @Query = 'SELECT * FROM ' + QUOTENAME(@table_name)
+ ' WHERE ' + QUOTENAME(@col_name) + ' = @val';
EXEC sp_executesql @Query, N'@val NVARCHAR(20)', @val = @col_value;
END
调用:
sql
DECLARE @table NVARCHAR(20) = 'ITCompanyInNepal',
@col NVARCHAR(20) = 'Headquarter',
@val NVARCHAR(20) = 'USA';
EXEC sp_dynamicSQL @table, @col, @val;
- 使用 QUOTENAME 避免注入
- 复杂场景可拼接更长脚本,实现多表/多条件动态查询
第50.4节 带 OUTPUT 参数的存储过程
存储过程可通过 OUTPUT 关键字返回值。
- 单输出参数示例
sql
CREATE PROC SprocWithOutParams
@InParam VARCHAR(30),
@OutParam VARCHAR(30) OUTPUT
AS
BEGIN
SET @OutParam = @InParam + ' must come out';
RETURN;
END
GO
-- 调用
DECLARE @Out VARCHAR(30);
EXEC SprocWithOutParams 'what goes in', @Out OUTPUT;
PRINT @Out;
- 多输出参数
sql
CREATE PROC SprocWithOutParams2
@InParam VARCHAR(30),
@Out1 VARCHAR(30) OUTPUT,
@Out2 VARCHAR(30) OUTPUT
AS
BEGIN
SET @Out1 = @InParam + ' must come out';
SET @Out2 = @InParam + ' must come out';
RETURN;
END
GO
-- 调用
DECLARE @O1 VARCHAR(30), @O2 VARCHAR(30);
EXEC SprocWithOutParams2 'what goes in', @O1 OUTPUT, @O2 OUTPUT;
PRINT @O1; PRINT @O2;
第50.5节 简单循环(遍历)
先准备临时数据:
sql
SELECT
o.name,
ROW_NUMBER() OVER (ORDER BY o.name) AS rn
INTO #systables
FROM sys.objects o
WHERE o.type = 'S';
循环脚本:
sql
DECLARE @rn INT = 1, @maxRn INT = (SELECT MAX(rn) FROM #systables);
DECLARE @tablename SYSNAME;
WHILE @rn <= @maxRn
BEGIN
SELECT @tablename = name, @rn = @rn + 1
FROM #systables
WHERE rn = @rn;
PRINT @tablename;
END
第50.6节 简单循环(指定次数打印)
sql
CREATE PROC SprocWithSimpleLoop
@SayThis VARCHAR(30),
@ThisManyTimes INT
AS
BEGIN
WHILE @ThisManyTimes > 0
BEGIN
PRINT @SayThis;
SET @ThisManyTimes = @ThisManyTimes - 1;
END
RETURN;
END
GO
调用:
sql
EXEC SprocWithSimpleLoop 'Hello, World!', 3;
输出:
Hello, World!
Hello, World!
Hello, World!