更强的安全性
多个用户和客户端程序可以通过过程对基础数据库对象执行操作,即使用户和程序对这些基础对象没有直接权限。 过程控制执行哪些进程和活动,并且保护基础数据库对象。 这消除在了单独的对象级别授予权限的要求,并且简化了安全层。
可在 CREATE PROCEDURE 语句中指定 EXECUTE AS 子句以便实现对其他用户的模拟,或者使用户或应用程序无需针对基础对象和命令的直接权限,即可执行某些数据库活动。 例如,某些操作(如 TRUNCATE TABLE)没有可授予的权限。 若要执行 TRUNCATE TABLE,用户必须对指定表具有 ALTER 权限。 授予用户对表的 ALTER 权限可能不是最佳方法,因为用户将拥有超出截断表的能力的权限。 通过将 TRUNCATE TABLE 语句纳入模块中并指定该模块作为一个有权修改表的用户执行,您可以将截断表的权限扩展至授予其对模块的 EXECUTE 权限的用户。
在通过网络调用过程时,只有对执行过程的调用是可见的。 因此,恶意用户无法看到表和数据库对象名称、嵌入自己的 Transact-SQL 语句或搜索关键数据。
使用过程参数有助于避免 SQL 注入攻击。 因为参数输入被视作文字值而非可执行代码,所以,攻击者将命令插入过程内的 Transact-SQL 语句并损害安全性将更为困难。
可以对过程进行加密,这有助于对源代码进行模糊处理。 有关详细信息,请参阅 SQL Server Encryption。
存储过程的类型
用户定义
用户定义的过程可在用户定义的数据库中创建,或者在除了 Resource 数据库之外的所有系统数据库中创建。 该过程可在 Transact-SQL 中开发,或者作为对 Microsoft .NET Framework 公共语言运行时 (CLR) 方法的引用开发。
临时
临时过程是用户定义过程的一种形式。 临时过程与永久过程相似,只是临时过程存储于 tempdb中。 临时过程有两种类型:本地过程和全局过程。 它们在名称、可见性以及可用性上有区别。 本地临时过程的名称以单个数字符号 (#) 开头;它们仅对当前的用户连接是可见的;当用户关闭连接时被删除。 全局临时过程的名称以两个数字符号 (##) 开头,创建后对任何用户都是可见的,并且在使用该过程的最后一个会话结束时被删除。
系统
系统过程是 SQL Server 随附的。 它们物理上存储在内部隐藏的 Resource 数据库中,但逻辑上出现在每个系统定义数据库和用户定义数据库的 sys 架构中。 此外, msdb 数据库还在 dbo 架构中包含用于计划警报和作业的系统存储过程。 因为系统过程以前缀 sp_ 开头,所以,我们建议你在命名用户定义过程时不要使用此前缀。 有关系统过程的完整列表,请参阅系统存储过程 (Transact-SQL)
SQL Server 支持在 SQL Server 和外部程序之间提供一个接口以实现各种维护活动的系统过程。 这些扩展过程使用 xp_ 前缀。 有关扩展过程的完整列表,请参阅常规扩展存储过程 (Transact-SQL)。
扩展的用户定义
扩展过程允许你使用编程语言(例如 C)创建外部例程。这些过程是指 SQL Server 的实例可以动态加载和运行的 DLL。
备注
SQL Server 的未来版本中将删除扩展存储过程。 请不要在新的开发工作中使用该功能,并尽快修改当前还在使用该功能的应用程序。 请改为创建 CLR 过程。 此方法提供了更为可靠和安全的替代方法来编写扩展过程。
在查询编辑器中创建过程
-
在 “对象资源管理器” 中,连接到 数据库引擎的实例。
-
从 “文件” 菜单中,单击 “新建查询” 。
-
将以下示例复制并粘贴到查询窗口中,然后单击“执行” 。 该示例将使用其他过程名称创建与上述相同的存储过程。
SQL复制
USE AdventureWorks2012; GO CREATE PROCEDURE HumanResources.uspGetEmployeesTest2 @LastName nvarchar(50), @FirstName nvarchar(50) AS SET NOCOUNT ON; SELECT FirstName, LastName, Department FROM HumanResources.vEmployeeDepartmentHistory WHERE FirstName = @FirstName AND LastName = @LastName AND EndDate IS NULL; GO
-
若要运行该过程,请将以下示例复制并粘贴到一个新的查询窗口中,然后单击 “执行” 。 请注意,将显示指定参数值的不同方法。
EXECUTE HumanResources.uspGetEmployeesTest2 N'Ackerman', N'Pilar'; -- Or EXEC HumanResources.uspGetEmployeesTest2 @LastName = N'Ackerman', @FirstName = N'Pilar'; GO -- Or EXECUTE HumanResources.uspGetEmployeesTest2 @FirstName = N'Pilar', @LastName = N'Ackerman'; GO
修改存储过程
限制和局限
Transact-SQL 存储过程修改为 CLR 存储过程,反之亦然。
如果原来的过程定义是使用 WITH ENCRYPTION 或 WITH RECOMPILE 创建的,那么只有在 ALTER PROCEDURE 语句中也包含这些选项时,这些选项才有效。
权限
要求对过程具有 ALTER PROCEDURE 权限。
使用 Transact-SQL
若要使用 T-SQL 命令修改过程:
-
在 “对象资源管理器” 中,连接到 数据库引擎 的实例,然后展开该实例。
-
展开 “数据库” ,然后展开过程所属的数据库。 或者,在工具栏上,从可用数据库列表中选择该数据库。 对于此示例,选择
AdventureWorks2019
数据库。 -
在“文件”菜单上,选择“新建查询” 。
-
复制以下示例并将其粘贴到查询编辑器中。 此示例创建
uspVendorAllInfo
过程,该过程返回 Adventure Works Cycles 数据库中所有供应商的名称、所提供的产品、信用等级以及可用性。SQL复制
IF OBJECT_ID ( 'Purchasing.uspVendorAllInfo', 'P' ) IS NOT NULL DROP PROCEDURE Purchasing.uspVendorAllInfo; GO CREATE PROCEDURE Purchasing.uspVendorAllInfo WITH EXECUTE AS CALLER AS SET NOCOUNT ON; SELECT v.Name AS Vendor, p.Name AS 'Product name', v.CreditRating AS 'Rating', v.ActiveFlag AS Availability FROM Purchasing.Vendor v INNER JOIN Purchasing.ProductVendor pv ON v.BusinessEntityID = pv.BusinessEntityID INNER JOIN Production.Product p ON pv.ProductID = p.ProductID ORDER BY v.Name ASC; GO
重要
删除再重新创建现有存储过程是,会删除已显式授予该存储过程的权限。 请改为使用 ALTER。
-
在“文件”菜单上,选择“新建查询” 。
-
复制以下示例并将其粘贴到查询编辑器中。 该示例修改
uspVendorAllInfo
过程。 这会删除EXECUTE AS CALLER
子句,并且将过程的主体修改为只返回提供指定产品的供应商。LEFT
和CASE
函数自定义结果集的外观。SQL复制
ALTER PROCEDURE Purchasing.uspVendorAllInfo @Product varchar(25) AS SET NOCOUNT ON; SELECT LEFT(v.Name, 25) AS Vendor, LEFT(p.Name, 25) AS 'Product name', 'Rating' = CASE v.CreditRating WHEN 1 THEN 'Superior' WHEN 2 THEN 'Excellent' WHEN 3 THEN 'Above average' WHEN 4 THEN 'Average' WHEN 5 THEN 'Below average' ELSE 'No rating' END , Availability = CASE v.ActiveFlag WHEN 1 THEN 'Yes' ELSE 'No' END FROM Purchasing.Vendor AS v INNER JOIN Purchasing.ProductVendor AS pv ON v.BusinessEntityID = pv.BusinessEntityID INNER JOIN Production.Product AS p ON pv.ProductID = p.ProductID WHERE p.Name LIKE @Product ORDER BY v.Name ASC; GO
-
若要将修改项保存到过程定义中,请在“查询”菜单上选择“执行” 。
-
若要将更新后的过程定义另存为 Transact-SQL 脚本,请在“文件”菜单上选择“另存为”。 接受该文件名或将其替换为新的名称,再选择“保存”。
-
若要运行修改的存储过程,请执行以下示例。
SQL复制
EXEC Purchasing.uspVendorAllInfo N'LL Crankarm'; GO
删除存储过程
限制和局限
如果依赖对象和脚本尚未更新以反映过程的删除,则删除过程可能会导致这些对象和脚本失败。 但是,如果创建了具有相同名称和参数的新过程来替换已被删除的过程,那么引用该过程的其他对象仍能成功处理。 有关详细信息,请参阅 查看存储过程的依赖关系。
权限
需要拥有对该过程所属架构的 ALTER 权限,或对该过程的 CONTROL 权限。
使用 Transact-SQL
-
在 “对象资源管理器” 中,连接到 数据库引擎 的实例,然后展开该实例。
-
展开 “数据库” 、过程所属的数据库,或者从工具栏,从可用数据库列表选择该数据库。
-
在“文件”菜单上,选择“新建查询”。
-
获取要在当前数据库中删除的存储过程的名称。 从对象资源管理器,展开 “可编程性” ,再展开 “存储过程” 。 或者,在查询编辑器中,运行以下语句:
SQL复制
SELECT name AS procedure_name , SCHEMA_NAME(schema_id) AS schema_name , type_desc , create_date , modify_date FROM sys.procedures;
-
将以下示例复制并粘贴到查询编辑器,然后插入要从当前数据库中删除的存储过程名称。
SQL复制
DROP PROCEDURE [<stored procedure name>]; GO
-
从所有依赖对象和脚本中删除对该过程的引用。
執行存儲過程
有两种不同方法执行存储过程。 第一种方法和最常见的方法供应用程序或用户调用过程。 第二种方法是将过程设置为在启动 SQL Server 实例时自动运行。 当应用程序或用户调用过程时,调用中显式声明了 Transact-SQL EXECUTE 或 EXEC 关键字。 如果过程是 Transact-SQL 批处理中的第一条语句,那么不使用关键字 EXEC 也可调用并执行此过程。
与系统过程名称匹配时使用调用数据库排序规则。 因此,在过程调用中始终使用系统过程名称的正确大小写形式。
若要显示确切的系统过程名称,请查询 sys.system_objects 和 sys.system_parameters 目录视图。
如果用户定义的过程与系统过程同名,则可能不会执行用户定义的过程。
执行系统存储过程
系统过程以前缀 sp_
开头。 因为从逻辑意义上讲,这些过程出现在所有用户定义的数据库和系统定义的数据库中,所以可以从任何数据库执行这些过程,而不必完全限定过程名称。 但是,建议使用 sys
架构名称对所有系统过程名称进行架构限定,以防止名称冲突。 以下示例说明调用系统过程的推荐方法。
SQL复制
EXEC sys.sp_who;
执行用户定义存储过程
当执行用户定义的过程时,我们建议使用架构名称限定过程名称。 这种做法使性能得到小幅提升,因为 数据库引擎 不必搜索多个架构。 如果某个数据库在多个架构中具有同名过程,则这还可以防止执行错误的过程。
以下示例说明执行用户定义过程的推荐方法。 请注意,此过程接受一个输入参数。 有关指定输入参数和输出参数的信息,请参阅 指定参数。
SQL复制
USE AdventureWorks2019;
GO
EXEC dbo.uspGetEmployeeManagers @BusinessEntityID = 50;
GO
-或-
SQL复制
EXEC AdventureWorks2019.dbo.uspGetEmployeeManagers 50;
GO
如果指定了非限定的用户定义过程,则 数据库引擎 按以下顺序搜索此过程:
-
sys
架构。 -
调用方的默认架构(如果它在批处理或动态 SQL 中执行)。 或者,如果非限定的过程名称出现在另一个过程定义的主体中,则接着搜索包含后一过程的架构。
-
当前数据库中的
dbo
架构。
自动执行存储过程
在每次启动 SQL Server 时将执行标记为自动执行的过程,并在启动过程期间中恢复 master
数据库。 将这些过程设置为自动执行对执行数据库维护操作或使这些过程作为后台进程连续运行很有用。 自动执行的另一个用途是使该过程执行 tempdb
中的系统或维护任务,如创建一个全局临时表。 这将确保在 SQL Server 启动过程中重新创建 tempdb
时,始终存在这样一个临时表。
自动执行的过程使用与固定服务器角色 sysadmin 的成员相同的权限进行操作。 该过程生成的所有错误消息都将写入 SQL Server 错误日志。
虽然对启动过程的数目没有限制,但是请注意,在执行时每个启动过程将占用一个工作线程。 如果必须在启动时执行多个过程,但不需要并行执行,则可以指定一个过程作为启动过程,让该过程调用其他过程。 这样就只占用一个工作线程。
提示
请勿从自动执行的过程中返回任何结果集。 因为该过程是由 SQL Server 而不是由应用程序或用户执行的,所以结果集将无处可去。
设置、清除和控制自动执行
只有系统管理员 (sa
) 可以将过程标记为自动执行。 另外,该过程必须在 sa
拥有的 master
数据库中,而且不能有输入或输出参数。
使用 sp_procoption 可以:
-
将现有过程指定为启动过程。
-
阻止过程在 SQL Server 启动时执行。
使用 SQL Server Management Studio
执行存储过程
-
在 “对象资源管理器” 中,连接到 SQL Server 数据库引擎的实例,再依次展开该实例、 “数据库” 。
-
展开所需的数据库,然后依次展开 “可编程性” 和 “存储过程” 。
-
右键单击所需的用户定义存储过程,然后选择“执行存储过程”。
-
在 “执行过程” 对话框中,为每个参数指定一个值以及它是否应传递 Null 值。
Parameter
指示参数的名称。数据类型
指示参数的数据类型。输出参数
指示是否为输出参数。传递空值
将 NULL 作为参数值传递。值
在调用过程时键入参数的值。 -
若要执行存储过程,请选择“确定”。
“使用 Transact-SQL”
执行存储过程
-
连接到 数据库引擎。
-
在标准栏上,选择“新建查询”。
-
将以下示例复制并粘贴到查询窗口中,然后选择“执行”。 此示例演示如何执行应有一个参数的存储过程。 该示例执行
uspGetEmployeeManagers
存储过程,并将值 6 指定为@EmployeeID
参数。
SQL复制
USE AdventureWorks2019;
GO
EXEC dbo.uspGetEmployeeManagers 6;
GO
设置或清除自动执行的过程
启动过程必须位于 master
数据库中,并且不能包含 INPUT 或 OUTPUT 参数。 所有数据库恢复后将开始执行存储过程,并在开始时记录“恢复已完成”消息。
有关详细信息,请参阅 sp_procoption (Transact-SQL)。
-
连接到 数据库引擎。
-
在标准栏上,选择“新建查询”。
-
将以下示例复制并粘贴到查询窗口中,然后选择“执行”。 此示例演示如何使用 sp_procoption 设置过程自动执行。
SQL复制
EXEC sp_procoption @ProcName = N'<procedure name>'
, @OptionName = 'startup'
, @OptionValue = 'on';
GO
阻止过程自动执行
-
连接到 数据库引擎。
-
在标准栏上,选择“新建查询”。
-
将以下示例复制并粘贴到查询窗口中,然后选择“执行”。 此示例说明如何使用 sp_procoption 阻止过程自动执行。
SQL复制
EXEC sp_procoption @ProcName = N'<procedure name>'
, @OptionName = 'startup'
, @OptionValue = 'off';
GO