EXECUTE (Transact-SQL)
执行 Transact-SQL 批中的命令字符串、字符串或执行下列模块之一:系统存储过程、用户定义存储过程、标量值用户定义函数或扩展存储过程。
安全说明 |
---|
在使用字符串调用 EXECUTE 之前,请先验证该字符串。永远不要执行由未经验证的用户输入构造的命令。有关详细信息,请参阅 SQL 注入。 |
SQL Server 扩展了 EXECUTE 语句,以使其可用于向链接服务器发送传递命令。此外,还可以显式设置执行字符串或命令的上下文。
语法
Execute a stored procedure or function
[ { EXEC | EXECUTE } ]
{
[ @return_status = ]
{ module_name [ ;number ] | @module_name_var }
[ [ @parameter = ] { value
| @variable [ OUTPUT ]
| [ DEFAULT ]
}
]
[ ,...n ]
[ WITH RECOMPILE ]
}
[;]
Execute a character string
{ EXEC | EXECUTE }
( { @string_variable | [ N ]'tsql_string' } [ + ...n ] )
[ AS { LOGIN | USER } = ' name ' ]
[;]
Execute a pass-through command against a linked server
{ EXEC | EXECUTE }
( { @string_variable | [ N ] 'command_string [ ? ]' } [ + ...n ]
[ { , { value | @variable [ OUTPUT ] } } [ ...n ] ]
)
[ AS { LOGIN | USER } = ' name ' ]
[ AT linked_server_name ]
[;]
参数
@return\_status
可选的整型变量,存储模块的返回状态。这个变量在用于 EXECUTE 语句前,必须在批处理、存储过程或函数中声明过。在用于调用标量值用户定义函数时,@return_status 变量可以为任意标量数据类型。
module_name
是要调用的存储过程或标量值用户定义函数的完全限定或者不完全限定名称。模块名称必须符合标识符规则。无论服务器的排序规则如何,扩展存储过程的名称总是区分大小写。用户可以执行在另一数据库中创建的模块,只要运行模块的用户拥有此模块或具有在该数据库中执行该模块的适当权限。用户可以在另一台运行 SQL Server 的服务器中执行模块,只要该用户有相应的权限使用该服务器(远程访问),并能在数据库中执行该模块。如果指定了服务器名称但没有指定数据库名称,则 SQL Server 数据库引擎会在用户的默认数据库中查找该模块。
; number
是可选整数,用于对同名的过程分组。该参数不能用于扩展存储过程。注意 后续版本的 Microsoft SQL Server 将删除该功能。 请避免在新的开发工作中使用该功能,并着手修改当前还在使用该功能的应用程序。
有关过程分组的详细信息,请参阅 CREATE PROCEDURE (Transact-SQL)。
@module\_name\_var
是局部定义的变量名,代表模块名称。@parameter
module_name 的参数,与在模块中定义的相同。参数名称前必须加上符号 (@)。在与 @parameter\_name=value 格式一起使用时,参数名和常量不必按它们在模块中定义的顺序提供。但是,如果对任何参数使用了 @parameter\_name=value 格式,则对所有后续参数都必须使用此格式。默认情况下,参数可为空值。
value
传递给模块或传递命令的参数值。如果参数名称没有指定,参数值必须以在模块中定义的顺序提供。对链接服务器执行传递命令时,参数值的顺序取决于链接服务器的 OLE DB 访问接口。大多数 OLE DB 访问接口按从左到右的顺序将值绑定到参数。
如果参数值是一个对象名、字符串或由数据库名称或架构名称限定,则整个名称必须用单引号括起来。如果参数值是一个关键字,则该关键字必须用双引号括起来。
如果在模块中定义了默认值,用户执行该模块时可以不必指定参数。
默认值也可以为 NULL。通常,模块定义会指定当参数值为 NULL 时应该执行的操作。
@variable
是用来存储参数或返回参数的变量。OUTPUT
指定模块或命令字符串返回一个参数。该模块或命令字符串中的匹配参数也必须已使用关键字 OUTPUT 创建。使用游标变量作为参数时使用该关键字。如果 value 定义为对链接服务器执行的模块的 OUTPUT,则 OLE DB 访问接口对相应 @parameter 所执行的任何更改都将在此模块执行结束时复制回此变量。
如果正在使用 OUTPUT 参数,并且使用的目的是在执行调用的批处理或模块内的其他语句中使用其返回值,则此参数的值必须作为变量传递,如 @parameter = @variable。如果一个参数在模块中没有定义为 OUTPUT 参数,则不能通过对该参数指定 OUTPUT 执行模块。不能使用 OUTPUT 将常量传递给模块;返回参数需要变量名称。在执行过程之前,必须声明变量的数据类型并赋值。
当对远程存储过程使用 EXECUTE 或对链接服务器执行传递命令时,OUTPUT 参数不能是任何大型对象 (LOB) 数据类型。
返回参数可以是 LOB 数据类型之外的任意数据类型。
DEFAULT
根据模块的定义,提供参数的默认值。当模块需要的参数值没有定义默认值并且缺少参数或指定了 DEFAULT 关键字,会出现错误。WITH RECOMPILE
执行模块后,强制编译、使用和放弃新计划。如果该模块存在现有查询计划,则该计划将保留在缓存中。如果所提供的参数为非典型参数或者数据有很大的改变,使用该选项。该选项不能用于扩展存储过程。建议尽量少使用该选项,因为它消耗较多系统资源。
注意 在调用使用 OPENDATASOURCE 语法的存储过程时,不能使用 WITH RECOMPILE。如果指定由四个部分组成的对象名,则忽略 WITH RECOMPILE 选项。
@string\_variable
是局部变量的名称。@string_variable 可以是任意 char、varchar、nchar 或 nvarchar 数据类型。其中包括 (max) 数据类型。[N] 'tsql_string'
常量字符串。tsql_string 可以是任意 nvarchar 或 varchar 数据类型。如果包含 N,则字符串将解释为 nvarchar 数据类型。AS <context_specification>
指定执行语句的上下文。有关详细信息,请参阅了解执行上下文。LOGIN
指定要模拟的上下文是登录名。模拟范围为服务器。USER
指定要模拟的上下文是当前数据库中的用户。模拟范围只限于当前数据库。对数据库用户的上下文切换不会继承该用户的服务器级别权限。重要提示 当到数据库用户的上下文切换处于活动状态时,任何对数据库外部资源的访问尝试都会导致语句失败。这包括 USE database 语句、分布式查询和使用三部分或四部分标识符引用其他数据库的查询。若要将上下文切换的范围扩展到当前数据库之外,请参阅使用 EXECUTE AS 扩展数据库模拟。
'name'
是有效的用户名或登录名。name 必须是 sysadmin 固定服务器角色的成员或分别为 sys.database_principals 或 sys.server_principals 的主体。name 不能为内置帐户,如 NT AUTHORITY\LocalService、NT AUTHORITY\NetworkService 或 NT AUTHORITY\LocalSystem。
有关详细信息,请参阅本主题后面的指定用户名或登录名。
[N] 'command_string'
常量字符串,包含要传递给链接服务器的命令。如果包含 N,则字符串将解释为 nvarchar 数据类型。[?]
指示参数,该参数的值在 EXEC('…', <arg-list>) AT <linkedsrv> 语句中所使用的 <arg-list> 传递命令中提供。AT linked_server_name
指定对 linked_server_name 执行 command_string,并将结果(如果有)返回客户端。linked_server_name 必须引用本地服务器中的现有链接服务器定义。链接服务器使用 sp_addlinkedserver 定义。
注释
可以使用 value 或 @parameter\_name = value 来提供参数。参数不是事务的一部分,如果在以后将回滚的事务中更改了参数,则此参数的值不会恢复为其以前的值。返回给调用方的值总是模块返回时的值。
当一个模块调用其他模块或通过引用公共语言运行时 (CLR) 模块、用户定义类型或聚合执行托管代码时,将出现嵌套。当开始执行调用模块或托管代码引用时,嵌套级别将增加,而当调用模块或托管代码引用完成时,嵌套级别将减少。嵌套级别最高为 32 级,超过 32 级时,会导致整个调用链失败。当前的嵌套级别存储在 @@NESTLEVEL 系统函数中。
因为远程存储过程和扩展存储过程不在事务的范围内(除非在 BEGIN DISTRIBUTED TRANSACTION 语句中发出或者是和不同的配置选项一起使用),所以通过调用执行的命令不能回滚。有关详细信息,请参阅系统存储过程 (Transact-SQL) 和 BEGIN DISTRIBUTED TRANSACTION (Transact-SQL)。
当使用游标变量时,如果执行的过程传递一个分配有游标的游标变量,就会出错。
在执行模块时,如果语句是批处理中的第一个语句,则不一定要指定 EXECUTE 关键字。
在存储过程中使用 EXECUTE
在执行存储过程时,如果语句是批处理中的第一个语句,则不一定要指定 EXECUTE 关键字。
SQL Server 系统存储过程以字符 sp_ 开头。这些存储过程物理上存储在资源数据库中,但逻辑上出现在每个系统数据库和用户定义数据库的 sys 架构中。在批处理或模块(如用户定义存储过程或函数)中执行系统存储过程时,建议使用 sys 架构名称限定存储过程名称。
SQL Server 系统扩展存储过程以字符 xp_ 开头,这些存储过程包含在 master 数据库的 dbo 架构中。在批处理或模块(如用户定义存储过程或函数)内执行系统扩展存储过程时,建议使用 master.dbo 限定存储过程名称。
在批处理或模块(如用户定义存储过程或函数)内执行用户定义存储过程时,建议使用架构名限定存储过程名称。建议不要使用与系统存储过程相同的名称命名用户定义存储过程。有关执行存储过程的详细信息,请参阅执行存储过程(数据库引擎)。
使用带字符串的 EXECUTE 命令
在 SQL Server 的早期版本中,字符串限制为 8,000 字节。这要求连接长字符串,以便动态执行。在 SQL Server 中,可以指定 varchar(max) 和 nvarchar(max) 数据类型,它们允许字符串使用多达 2 GB 数据。
数据库上下文的更改只在 EXECUTE 语句结束前有效。例如,运行下面这条语句中的 EXEC 之后,数据库上下文将为 master。
USE master; EXEC ('USE AdventureWorks; SELECT EmployeeID, Title FROM HumanResources.Employee;');
上下文切换
可以使用 AS { LOGIN | USER } = ' name ' 子句切换动态语句的执行上下文。当将上下文切换指定为 EXECUTE ('string') AS <context_specification> 时,上下文切换的持续时间限制为执行查询的范围。有关详细信息,请参阅了解上下文切换。
指定用户名或登录名
AS { LOGIN | USER } = ' name ' 中指定的用户名或登录名必须分别为 sys.database_principals 或 sys.server_principals 的主体,否则该语句将失败。此外,还必须为该主体授予 IMPERSONATE 权限。除非调用方是数据库所有者或 sysadmin 固定服务器角色的成员,否则,即使在用户通过 Windows 组成员身份访问数据库或 SQL Server 实例时,也必须存在该主体。例如,假设条件如下:
CompanyDomain\SQLUsers 组具有对 Sales 数据库的访问权限。
CompanyDomain\SqlUser1 是 SQLUsers 的成员,因此具有对 Sales 数据库的隐式访问权限。
尽管 CompanyDomain\SqlUser1 可以通过 SQLUsers 组的成员身份访问数据库,但 EXECUTE @string\_variable AS USER = 'CompanyDomain\SqlUser1' 语句仍会失败,因为 CompanyDomain\SqlUser1 不是数据库的主体。
最佳实践
指定具有执行语句或模块中定义的操作所需的最低权限的登录名或用户。例如,如果只需数据库级别权限,则不要指定拥有服务器级别权限的登录名;如果不需要相应权限,也不要指定数据库所有者帐户。
权限
运行 EXECUTE 语句无需权限。但是,需要对 EXECUTE 字符串内引用的安全对象具有权限。例如,如果字符串包含 INSERT 语句,则 EXECUTE 语句的调用方对目标表必须具有 INSERT 权限。在遇到 EXECUTE 语句时,即使 EXECUTE 语句包含于模块内,也将检查权限。
模块的 EXECUTE 权限默认授予该模块的所有者,该所有者可以将此权限转让给其他用户。当运行一个执行字符串的模块时,系统会在执行该模块的用户上下文中而不是在创建该模块的用户上下文中检查权限。但是,如果同一用户拥有调用模块和被调用模块,则不对后者执行 EXECUTE 权限检查。有关详细信息,请参阅所有权链。
如果模块访问其他数据库对象,则当拥有对该模块的 EXECUTE 权限并且以下任一情况存在时,执行将成功:
模块被标记为 EXECUTE AS USER 或 SELF,并且模块所有者对被引用对象具有相应权限。
模块被标记为 EXECUTE AS CALLER,并且您对对象具有相应权限。
模块被标记为 EXECUTE AS user_name,并且 user_name 对对象具有相应权限。
上下文切换权限
若要对某登录名指定 EXECUTE AS,调用方必须具有对所指定登录名的 IMPERSONATE 权限。若要对某数据库用户指定 EXECUTE AS,调用方必须具有对所指定用户名的 IMPERSONATE 权限。如果未指定执行上下文或指定了 EXECUTE AS CALLER,则无需 IMPERSONATE 权限。
示例
A. 使用 EXECUTE 传递单个参数
uspGetEmployeeManagers 存储过程需要一个参数 (@EmployeeID)。以下示例执行 uspGetEmployeeManagers 存储过程,以 Employee ID 6 作为参数值。
USE AdventureWorks;
GO
EXEC dbo.uspGetEmployeeManagers 6;
GO
在执行过程中变量可以显式命名:
EXEC dbo.uspGetEmployeeManagers @EmployeeID = 6;
GO
如果以下语句为批处理、osql 或 sqlcmd 脚本中的第一个语句,则无需 EXEC。
dbo.uspGetEmployeeManagers 6;
GO
--Or
dbo.uspGetEmployeeManagers @EmployeeID = 6;
GO
B. 使用多个参数
以下示例执行 spGetWhereUsedProductID 存储过程。该存储过程将传递两个参数:第一个参数为产品 ID (819),第二个参数 @CheckDate, 是 datetime 值。
USE AdventureWorks;
GO
DECLARE @CheckDate datetime;
SET @CheckDate = GETDATE();
EXEC dbo.uspGetWhereUsedProductID 819, @CheckDate;
GO
C.使用带变量的 EXECUTE 'tsql_string' 语句
下面的示例说明了 EXECUTE 如何处理动态生成的包含变量的字符串。该示例创建 tables_cursor 游标以保存 AdventureWorks 数据库中所有用户定义表的列表,然后使用该列表重新生成对表的全部索引。
USE AdventureWorks;
GO
DECLARE tables_cursor CURSOR
FOR
SELECT s.name, t.name
FROM sys.objects AS t
JOIN sys.schemas AS s ON s.schema_id = t.schema_id
WHERE t.type = 'U';
OPEN tables_cursor;
DECLARE @schemaname sysname;
DECLARE @tablename sysname;
FETCH NEXT FROM tables_cursor INTO @schemaname, @tablename;
WHILE (@@FETCH_STATUS <> -1)
BEGIN;
EXECUTE ('ALTER INDEX ALL ON ' + @schemaname + '.' + @tablename + ' REBUILD;');
FETCH NEXT FROM tables_cursor INTO @schemaname, @tablename;
END;
PRINT 'The indexes on all tables have been rebuilt.';
CLOSE tables_cursor;
DEALLOCATE tables_cursor;
GO
D.对远程存储过程使用 EXECUTE 语句
以下示例在远程服务器 SQLSERVER1 上执行 uspGetEmployeeManagers 存储过程,然后在 @retstat 中存储指示成功或失败的返回状态。
DECLARE @retstat int;
EXECUTE @retstat = SQLSERVER1.AdventureWorks.dbo.uspGetEmployeeManagers @EmployeeID = 6;
E. 使用带存储过程变量的 EXECUTE 语句
以下示例创建一个代表存储过程名称的变量。
DECLARE @proc_name varchar(30);
SET @proc_name = 'sys.sp_who';
EXEC @proc_name;
F. 使用带 DEFAULT 的 EXECUTE
以下示例创建了一个存储过程,第一个和第三个参数具有默认值。当运行该过程时,如果调用时没有传递值或者指定了默认值,这些默认值就会赋给第一个和第三个参数。请注意,DEFAULT 关键字有多种使用方法。
USE AdventureWorks;
GO
IF OBJECT_ID(N'dbo.ProcTestDefaults', N'P')IS NOT NULL
DROP PROCEDURE dbo.ProcTestDefaults;
GO
-- Create the stored procedure.
CREATE PROCEDURE dbo.ProcTestDefaults (
@p1 smallint = 42,
@p2 char(1),
@p3 varchar(8) = 'CAR')
AS
SET NOCOUNT ON;
SELECT @p1, @p2, @p3
;
GO
Proc_Test_Defaults 存储过程可使用多种组合执行。
-- Specifying a value only for one parameter (@p2).
EXECUTE dbo.ProcTestDefaults @p2 = 'A';
-- Specifying a value for the first two parameters.
EXECUTE dbo.ProcTestDefaults 68, 'B';
-- Specifying a value for all three parameters.
EXECUTE dbo.ProcTestDefaults 68, 'C', 'House';
-- Using the DEFAULT keyword for the first parameter.
EXECUTE dbo.ProcTestDefaults @p1 = DEFAULT, @p2 = 'D';
-- Specifying the parameters in an order different from the order defined in the procedure.
EXECUTE dbo.ProcTestDefaults DEFAULT, @p3 = 'Local', @p2 = 'E';
-- Using the DEFAULT keyword for the first and third parameters.
EXECUTE dbo.ProcTestDefaults DEFAULT, 'H', DEFAULT;
EXECUTE dbo.ProcTestDefaults DEFAULT, 'I', @p3 = DEFAULT;
G. 使用带 AT linked_server_name 的 EXECUTE
以下示例将一个命令字符串传递给远程服务器。先创建一个链接服务器 SeattleSales,它指向 SQL Server 的另一个实例,然后对该链接服务器执行 DDL 语句 (CREATE TABLE)。
EXEC sp_addlinkedserver 'SeattleSales', 'SQL Server'
GO
EXECUTE ( 'CREATE TABLE AdventureWorks.dbo.SalesTbl
(SalesID int, SalesName varchar(10)) ; ' ) AT SeattleSales;
GO
H. 使用 EXECUTE WITH RECOMPILE
以下示例执行 Proc_Test_Defaults 存储过程,并在执行模块后强制编译、使用和放弃一个新查询计划。
EXECUTE dbo.Proc_Test_Defaults @p2 = 'A' WITH RECOMPILE;
GO
I. 对用户定义函数使用 EXECUTE
以下示例执行 ufnGetSalesOrderStatusText 标量用户定义函数。该语句使用 @returnstatus 变量存储函数的返回值。函数需要一个输入参数 @Status。该参数定义为 tinyint 数据类型。
USE AdventureWorks;
GO
DECLARE @returnstatus nvarchar(15);
SET @returnstatus = NULL;
EXEC @returnstatus = dbo.ufnGetSalesOrderStatusText @Status = 2;
PRINT @returnstatus;
GO
J. 使用 EXECUTE 查询链接服务器上的 Oracle 数据库
以下示例在远程 Oracle 服务器上执行几个 SELECT 语句。示例开始时添加 Oracle 服务器作为链接服务器,并创建链接服务器登录。
-- Setup the linked server.
EXEC sp_addlinkedserver
@server='ORACLE',
@srvproduct='Oracle',
@provider='OraOLEDB.Oracle',
@datasrc='ORACLE10';
EXEC sp_addlinkedsrvlogin
@rmtsrvname='ORACLE',
@useself='false',
@locallogin=null,
@rmtuser='scott',
@rmtpassword='tiger';
EXEC sp_serveroption 'ORACLE', 'rpc out', true;
GO
-- Execute several statements on the linked Oracle server.
EXEC ( 'SELECT * FROM scott.emp') AT ORACLE;
GO
EXEC ( 'SELECT * FROM scott.emp WHERE MGR = ?', 7902) AT ORACLE;
GO
DECLARE @v INT;
SET @v = 7902;
EXEC ( 'SELECT * FROM scott.emp WHERE MGR = ?', @v) AT ORACLE;
GO
K. 使用 EXECUTE AS USER 将上下文切换为其他用户
以下示例执行 Transact-SQL 字符串创建表并指定 AS USER 子句将语句的执行上下文从调用方切换为 User1。语句运行时,数据库引擎将检查 User1 的权限。User1 必须为数据库中的用户,必须具有在 Sales 架构中创建表的权限,否则语句将失败。
USE AdventureWorks;
GO
EXECUTE ('CREATE TABLE Sales.SalesTable (SalesID int, SalesName varchar(10));')
AS USER = 'User1';
GO
L. 以 EXECUTE 和 AT linked_server_name 使用参数
以下示例使用问号 (?) 占位符作为参数向远程服务器传递命令字符串。该示例先创建一个链接服务器 SeattleSales,它指向另一个 SQL Server 实例,然后对该链接服务器执行 SELECT 语句。SELECT 语句使用问号作为 ProductID 参数 (952)(该参数在语句后提供)的占位符。
-- Setup the linked server.
EXEC sp_addlinkedserver 'SeattleSales', 'SQL Server'
GO
-- Execute the SELECT statement.
EXECUTE ('SELECT ProductID, Name
FROM AdventureWorks.Production.Product
WHERE ProductID = ? ', 952) AT SeattleSales;
GO
请参阅