存储过程概要
存储过程是由一个或多个 Transact-SQL 语句或对 Microsoft .NET Framework 公共语言运行时 (CLR) 方法的引用构成的一个组。
存储过程优点
- 过程中的命令作为代码的单个批处理执行。可以减少服务器/客户端网络流量。
- 安全性,可对存储过程设置权限操作。
- 代码可重复使用
- 更容易维护
创建存储过程
-- Transact-SQL Syntax for Stored Procedures in SQL Server and Azure SQL Database CREATE [ OR ALTER ] { PROC | PROCEDURE } [schema_name.] procedure_name [ ; number ] [ { @parameter [ type_schema_name. ] data_type } [ VARYING ] [ = default ] [ OUT | OUTPUT | [READONLY] ] [ ,...n ] [ WITH <procedure_option> [ ,...n ] ] [ FOR REPLICATION ] AS { [ BEGIN ] sql_statement [;] [ ...n ] [ END ] } [;] <procedure_option> ::= [ ENCRYPTION ] [ RECOMPILE ] [ EXECUTE AS Clause ]
schema_name:过程所属架构的名称。 过程是绑定到架构的。 如果在创建过程时未指定架构名称,则自动分配正在创建过程的用户的默认架构。
procedure_name:过程的名称
number:适用范围
@ parameter:在过程中声明的参数。
data_type:参数的数据类型。
VARYING: 指定作为输出参数支持的结果集(由存储过程动态构造,内容可以变化)。仅适用于游标参数。
default: 参数的默认值。如果定义了默认值,不必指定该参数的值即可执行过程。默认值必须是常量或 NULL。如果过程将对该参数使用 LIKE 关键字,那么默认值中可以包含通配符(%、_、[] 和 [^])。
OUTPUT :表明参数是返回参数。该选项的值可以返回给 EXEC[UTE]。使用 OUTPUT 参数可将信息返回给调用过程。Text、ntext 和 image 参数可用作 OUTPUT 参数。使用 OUTPUT 关键字的输出参数可以是游标占位符。
READONLY:指示不能在过程的主体中更新或修改参数。 如果参数类型为表值类型,则必须指定 READONLY。
RECOMPILE:指示数据库引擎不缓存此过程的查询计划,这强制在每次执行此过程时都对该过程进行编译。
ENCRYPTION:指示 SQL Server 将 CREATE PROCEDURE 语句的原始文本转换为模糊格式
EXECUTE AS:指定在其中执行过程的安全上下文
FOR REPLICATION:指定为复制创建该过程。
存储过AS内容主要事项
使用 SET NOCOUNT ON 语句作为过程主体中的第一个语句。 也就是说,将其放置于紧接着 AS 关键字之后。 这会禁止显示在执行任何 SELECT、INSERT、UPDATE、MERGE 和 DELETE 语句后 SQL Server 发送回客户端的消息。
避免处理或返回过多的数据。
请避免使用 SELECT *
。 而是应指定所需的列名。 这样做可以避免停止过程执行的数据库引擎错误。
避免在返回许多行数据的 SELECT 语句中使用标量函数
避免函数包装在 WHERE 和 JOIN 子句中指定的列。
通过使用 BEGIN/COMMIT TRANSACTION 使用显式事务,使事务尽可能短。
使用 Transact-SQL TRY…CATCH 功能进行过程内的错误处理。 TRY…CATCH 可以封装整个 Transact-SQL 语句块。 这不仅产生更少的性能开销,还通过显著减少的编程,使错误报告更精确。
在过程主体中对 CREATE TABLE 或 ALTER TABLE Transact-SQL 语句引用的所有表列使用 DEFAULT 关键字。 这会禁止将 NULL 传递到不允许 Null 值的列。
使用将转换 Null 的修改语句并且包括从查询中删除含 Null 值的行的逻辑。
使用 UNION ALL 运算符来代替 UNION 或 OR 运算符,除非存在针对非重复值的特定需要。UNION ALL 运算符要求更少的处理开销,因为重复值不从结果集中筛选出来。
基本语法
CREATE PROCEDURE HumanResources.uspGetAllEmployees AS SET NOCOUNT ON; SELECT LastName, FirstName, JobTitle, Department FROM HumanResources.vEmployeeDepartment; GO SELECT * FROM HumanResources.vEmployeeDepartment; --执行 EXECUTE HumanResources.uspGetAllEmployees; GO -- Or EXEC HumanResources.uspGetAllEmployees; GO
返回多个结果集存储过程
CREATE PROCEDURE dbo.uspMultipleResults AS SELECT TOP(10) BusinessEntityID, Lastname, FirstName FROM Person.Person; SELECT TOP(10) CustomerID, AccountNumber FROM Sales.Customer; GO
带参数的存储过程
IF OBJECT_ID ( 'HumanResources.uspGetEmployees', 'P' ) IS NOT NULL DROP PROCEDURE HumanResources.uspGetEmployees; GO CREATE PROCEDURE HumanResources.uspGetEmployees @LastName nvarchar(50), @FirstName nvarchar(50) AS SET NOCOUNT ON; SELECT FirstName, LastName, JobTitle, Department FROM HumanResources.vEmployeeDepartment WHERE FirstName = @FirstName AND LastName = @LastName; GO --执行 EXECUTE HumanResources.uspGetEmployees N'Ackerman', N'Pilar'; -- Or EXEC HumanResources.uspGetEmployees @LastName = N'Ackerman', @FirstName = N'Pilar'; GO -- Or EXECUTE HumanResources.uspGetEmployees @FirstName = N'Pilar', @LastName = N'Ackerman'; GO -- Or, if this procedure is the first statement within a batch: HumanResources.uspGetEmployees N'Ackerman', N'Pilar';
使用带有通配符参数的过程
IF OBJECT_ID ( 'HumanResources.uspGetEmployees2', 'P' ) IS NOT NULL DROP PROCEDURE HumanResources.uspGetEmployees2; GO CREATE PROCEDURE HumanResources.uspGetEmployees2 @LastName nvarchar(50) = N'D%', @FirstName nvarchar(50) = N'%' AS SET NOCOUNT ON; SELECT FirstName, LastName, JobTitle, Department FROM HumanResources.vEmployeeDepartment WHERE FirstName LIKE @FirstName AND LastName LIKE @LastName; 执行 EXECUTE HumanResources.uspGetEmployees2; -- Or EXECUTE HumanResources.uspGetEmployees2 N'Wi%'; -- Or EXECUTE HumanResources.uspGetEmployees2 @FirstName = N'%'; -- Or EXECUTE HumanResources.uspGetEmployees2 N'[CK]ars[OE]n'; -- Or EXECUTE HumanResources.uspGetEmployees2 N'Hesse', N'Stefen'; -- Or EXECUTE HumanResources.uspGetEmployees2 N'H%', N'S%';
使用 OUTPUT 参数
IF OBJECT_ID ( 'Production.uspGetList', 'P' ) IS NOT NULL DROP PROCEDURE Production.uspGetList; GO CREATE PROCEDURE Production.uspGetList @Product varchar(40) , @MaxPrice money , @ComparePrice money OUTPUT , @ListPrice money OUT AS SET NOCOUNT ON; SELECT p.[Name] AS Product, p.ListPrice AS 'List Price' FROM Production.Product AS p JOIN Production.ProductSubcategory AS s ON p.ProductSubcategoryID = s.ProductSubcategoryID WHERE s.[Name] LIKE @Product AND p.ListPrice < @MaxPrice; -- Populate the output variable @ListPprice. SET @ListPrice = (SELECT MAX(p.ListPrice) FROM Production.Product AS p JOIN Production.ProductSubcategory AS s ON p.ProductSubcategoryID = s.ProductSubcategoryID WHERE s.[Name] LIKE @Product AND p.ListPrice < @MaxPrice); -- Populate the output variable @compareprice. SET @ComparePrice = @MaxPrice; GO --------------------------------------------------------------------- DECLARE @ComparePrice money, @Cost money ; EXECUTE Production.uspGetList '%Bikes%', 700, @ComparePrice OUT, @Cost OUTPUT IF @Cost <= @ComparePrice BEGIN PRINT 'These products can be purchased for less than $'+RTRIM(CAST(@ComparePrice AS varchar(20)))+'.' END ELSE PRINT 'The prices for all products in this category exceed $'+ RTRIM(CAST(@ComparePrice AS varchar(20)))+'.'; /* Product List Price -------------------------- ---------- Road-750 Black, 58 539.99 Mountain-500 Silver, 40 564.99 Mountain-500 Silver, 42 564.99 ... Road-750 Black, 48 539.99 Road-750 Black, 52 539.99 (14 row(s) affected) These items can be purchased for less than $700.00. */
使用表值参数
/* Create a table type. */ CREATE TYPE LocationTableType AS TABLE ( LocationName VARCHAR(50) , CostRate INT ); GO /* Create a procedure to receive data for the table-valued parameter. */ CREATE PROCEDURE usp_InsertProductionLocation @TVP LocationTableType READONLY AS SET NOCOUNT ON INSERT INTO [AdventureWorks2012].[Production].[Location] ([Name] ,[CostRate] ,[Availability] ,[ModifiedDate]) SELECT *, 0, GETDATE() FROM @TVP; GO /* Declare a variable that references the type. */ DECLARE @LocationTVP AS LocationTableType; /* Add data to the table variable. */ INSERT INTO @LocationTVP (LocationName, CostRate) SELECT [Name], 0.00 FROM [AdventureWorks2012].[Person].[StateProvince]; /* Pass the table variable data to a stored procedure. */ EXEC usp_InsertProductionLocation @LocationTVP; GO
捕获异常存储过程(存储过程的创建、修改、删除)
CREATE PROCEDURE Production.uspDeleteWorkOrder ( @WorkOrderID int ) AS SET NOCOUNT ON; BEGIN TRY BEGIN TRANSACTION -- Delete rows from the child table, WorkOrderRouting, for the specified work order. DELETE FROM Production.WorkOrderRouting WHERE WorkOrderID = @WorkOrderID; -- Delete the rows from the parent table, WorkOrder, for the specified work order. DELETE FROM Production.WorkOrder WHERE WorkOrderID = @WorkOrderID; COMMIT END TRY BEGIN CATCH -- Determine if an error occurred. IF @@TRANCOUNT > 0 ROLLBACK -- Return the error information. DECLARE @ErrorMessage nvarchar(4000), @ErrorSeverity int; SELECT @ErrorMessage = ERROR_MESSAGE(),@ErrorSeverity = ERROR_SEVERITY(); RAISERROR(@ErrorMessage, @ErrorSeverity, 1); END CATCH; GO EXEC Production.uspDeleteWorkOrder 13; /* Intentionally generate an error by reversing the order in which rows are deleted from the parent and child tables. This change does not cause an error when the procedure definition is altered, but produces an error when the procedure is executed. */ ALTER PROCEDURE Production.uspDeleteWorkOrder ( @WorkOrderID int ) AS BEGIN TRY BEGIN TRANSACTION -- Delete the rows from the parent table, WorkOrder, for the specified work order. DELETE FROM Production.WorkOrder WHERE WorkOrderID = @WorkOrderID; -- Delete rows from the child table, WorkOrderRouting, for the specified work order. DELETE FROM Production.WorkOrderRouting WHERE WorkOrderID = @WorkOrderID; COMMIT TRANSACTION END TRY BEGIN CATCH -- Determine if an error occurred. IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION -- Return the error information. DECLARE @ErrorMessage nvarchar(4000), @ErrorSeverity int; SELECT @ErrorMessage = ERROR_MESSAGE(),@ErrorSeverity = ERROR_SEVERITY(); RAISERROR(@ErrorMessage, @ErrorSeverity, 1); END CATCH; GO -- Execute the altered procedure. EXEC Production.uspDeleteWorkOrder 15; DROP PROCEDURE Production.uspDeleteWorkOrder;
其他
重命名
EXEC sp_rename 'HumanResources.uspGetAllEmployeesTest', 'uspEveryEmployeeTest';
查询创建文本
SELECT definition FROM sys.sql_modules WHERE object_id = (OBJECT_ID(N'AdventureWorks2012.dbo.uspLogError')); EXEC sp_helptext 'spt_values';
添加权限
GRANT EXECUTE ON OBJECT::HumanResources.uspUpdateEmployeeHireInfo TO Recruiting11; GO