sp_executesql (Transact-SQL)

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例Azure Synapse AnalyticsAnalytics Platform System (PDW)Microsoft Fabric 中的 SQL 分析终结点Microsoft Fabric 中的仓库

执行可以多次重复使用的 Transact-SQL 语句或批处理,或动态生成的语句。 Transact-SQL 语句或批处理可以包含嵌入参数。

注意

运行时编译的 Transact-SQL 语句可能会向恶意攻击公开应用程序。 使用 sp_executesql时应参数化查询。 有关详细信息,请参阅 SQL 注入

Transact-SQL 语法约定

语法

SQL Server、Azure SQL 数据库、Azure SQL 托管实例、Azure Synapse Analytics 和 Analytics Platform System(PDW)的语法。

sp_executesql [ @stmt = ] N'statement'
[
    [ , [ @params = ] N'@parameter_name data_type [ { OUT | OUTPUT } ] [ , ...n ]' ]
    [ , [ @param1 = ] 'value1' [ , ...n ] ]
]

本文中的 Transact-SQL 代码示例使用AdventureWorks2022示例数据库,可以从 Microsoft SQL Server 示例和社区项目主页下载该数据库。

参数

[ @stmt = ] N'statement'

包含 Transact-SQL 语句或批处理的 Unicode 字符串。 @stmt 必须是 Unicode 常量或 Unicode 变量。 不允许更复杂的 Unicode 表达式,例如将两个字符串与运算符连接在一起 + 。 不允许使用字符常量。 Unicode 常量必须带有 N前缀 。 例如,Unicode 常量 N'sp_who' 有效,但字符常量 'sp_who' 无效。 字符串的大小仅受可用数据库服务器内存限制。 在 64 位服务器中,字符串大小限制为 2 GB,即 nvarchar(max) 的最大大小。

@stmt 可以包含与变量名称具有相同格式的参数。 例如:

N'SELECT * FROM HumanResources.Employee WHERE EmployeeID = @IDParameter';

@stmt中包含的每个参数都必须在@params参数定义列表和参数值列表中都有相应的条目。

[ @params = ] N'@parameter_namedata_type [ ,... n ]'

一个字符串,其中包含嵌入 @stmt的所有参数的定义。字符串必须是 Unicode 常量或 Unicode 变量。 每个参数定义由参数名称和数据类型组成。 n 是指示更多参数定义的占位符。 必须在@params中定义@stmt中指定的每个参数。 如果@stmt中的 Transact-SQL 语句或批处理不包含参数,则不需要@params 此参数的默认值为 NULL

[ @param1 = ] 'value1'

参数字符串中定义的第一个参数的值。 该值可以是 Unicode 常量,也可以是 Unicode 变量。 必须为@stmt中包含的每个参数提供参数值。当 transact-SQL 语句或@stmt中的批处理没有参数时,不需要这些值。

{ OUT |OUTPUT }

指示参数是输出参数。 文本ntext图像 参数可用作 OUTPUT 参数,除非该过程是公共语言运行时 (CLR) 过程。 使用OUTPUT关键字 (keyword)的输出参数可以是游标占位符,除非过程是 CLR 过程。

[ ...n ]

额外参数值的占位符。 这些值只能为常量或变量, 值不能是更复杂的表达式,例如使用运算符生成的函数或表达式。

返回代码值

0 (成功)或非零(失败)。

结果集

从生成 SQL 字符串的所有 SQL 语句中返回结果集。

注解

sp_executesql 参数必须按本文前面“ 语法 ”部分中所述的特定顺序输入。 如果参数按顺序输入,则会出现错误消息。

sp_executesql 与批处理、名称和数据库上下文具有相同的行为 EXECUTE 。 在执行语句之前sp_executesql,不会编译 @stmt 参数中的 sp_executesqlTransact-SQL 语句或批处理。 然后,将编译并执行@stmt的内容,作为执行计划与调用sp_executesql的批处理的执行计划分开。 批处理 sp_executesql 不能引用在调用 sp_executesql的批处理中声明的变量。 批处理中的 sp_executesql 本地游标或变量对调用 sp_executesql的批处理不可见。 数据库上下文中的更改只持续到 sp_executesql 语句的结尾。

sp_executesql 当对语句的参数值更改是唯一的变体时,可以使用存储过程而不是存储过程来执行 Transact-SQL 语句。 因为 Transact-SQL 语句本身保持不变,仅参数值发生变化,所以 SQL Server 查询优化器可能重复使用首次执行时所生成的执行计划。 在此方案中,性能相当于存储过程的性能。

注意

若要提高性能,请使用语句字符串中的完全限定对象名称。

sp_executesql 支持将参数值与 Transact-SQL 字符串分开设置,如以下示例所示。

DECLARE @IntVariable INT;
DECLARE @SQLString NVARCHAR(500);
DECLARE @ParmDefinition NVARCHAR(500);

/* Build the SQL string once */
SET @SQLString = N'SELECT BusinessEntityID, NationalIDNumber, JobTitle, LoginID
       FROM AdventureWorks2022.HumanResources.Employee
       WHERE BusinessEntityID = @BusinessEntityID';
SET @ParmDefinition = N'@BusinessEntityID tinyint';
/* Execute the string with the first parameter value. */
SET @IntVariable = 197;

EXECUTE sp_executesql @SQLString,
    @ParmDefinition,
    @BusinessEntityID = @IntVariable;

/* Execute the same string with the second parameter value. */
SET @IntVariable = 109;

EXECUTE sp_executesql @SQLString,
    @ParmDefinition,
    @BusinessEntityID = @IntVariable;

输出参数也可用于 sp_executesql。 以下示例从示例数据库中的HumanResources.EmployeeAdventureWorks2022表中检索作业标题,并在输出参数@max_title中返回它。

DECLARE @IntVariable INT;
DECLARE @SQLString NVARCHAR(500);
DECLARE @ParmDefinition NVARCHAR(500);
DECLARE @max_title VARCHAR(30);

SET @IntVariable = 197;
SET @SQLString = N'SELECT @max_titleOUT = max(JobTitle)
   FROM AdventureWorks2022.HumanResources.Employee
   WHERE BusinessEntityID = @level';
SET @ParmDefinition = N'@level TINYINT, @max_titleOUT VARCHAR(30) OUTPUT';

EXECUTE sp_executesql @SQLString,
    @ParmDefinition,
    @level = @IntVariable,
    @max_titleOUT = @max_title OUTPUT;

SELECT @max_title;

能够替换参数 sp_executesql 比使用 EXECUTE 语句执行字符串具有以下优势:

  • 由于字符串中 sp_executesql Transact-SQL 语句的实际文本不会在执行之间更改,因此查询优化器可能与第二次执行中的 Transact-SQL 语句与为第一次执行生成的执行计划匹配。 因此,SQL Server 不必编译第二个语句。

  • Transact-SQL 字符串仅生成一次。

  • 整数参数按其本身格式指定。 不需要强制转换为 Unicode。

权限

要求 公共 角色具有成员身份。

示例

A. 执行 标准版LECT 语句

以下示例创建并执行包含名为 <a0SELECT/> 的嵌入参数的语句。

EXECUTE sp_executesql
    N'SELECT * FROM AdventureWorks2022.HumanResources.Employee
    WHERE BusinessEntityID = @level',
    N'@level TINYINT',
    @level = 109;

B. 执行动态生成的字符串

以下示例显示使用 sp_executesql 执行动态生成的字符串。 该示例中的存储过程用于向一组表中插入数据,这些表用于划分一年的销售数据。 每年每个月都有一个表,格式如下:

CREATE TABLE May1998Sales (
    OrderID INT PRIMARY KEY,
    CustomerID INT NOT NULL,
    OrderDate DATETIME NULL CHECK (DATEPART(yy, OrderDate) = 1998),
    OrderMonth INT CHECK (OrderMonth = 5),
    DeliveryDate DATETIME NULL,
    CHECK (DATEPART(mm, OrderDate) = OrderMonth)
);

此示例存储过程将动态生成并执行 INSERT 语句,以便向正确的表中插入新订单。 此示例使用订货日期生成应包含数据的表的名称,然后将此名称并入 INSERT 语句中。

注意

这是一个基本的示例 sp_executesql。 该示例不包含错误检查,并且不包括业务规则的检查,例如保证在表之间不复制订单编号。

CREATE PROCEDURE InsertSales @PrmOrderID INT,
    @PrmCustomerID INT,
    @PrmOrderDate DATETIME,
    @PrmDeliveryDate DATETIME
AS
DECLARE @InsertString NVARCHAR(500);
DECLARE @OrderMonth INT;

-- Build the INSERT statement.
SET @InsertString = 'INSERT INTO ' +
    /* Build the name of the table. */
    SUBSTRING(DATENAME(mm, @PrmOrderDate), 1, 3) +
    CAST(DATEPART(yy, @PrmOrderDate) AS CHAR(4)) + 'Sales' +
    /* Build a VALUES clause. */
    ' VALUES (@InsOrderID, @InsCustID, @InsOrdDate,' +
    ' @InsOrdMonth, @InsDelDate)';

/* Set the value to use for the order month because
   functions are not allowed in the sp_executesql parameter
   list. */
SET @OrderMonth = DATEPART(mm, @PrmOrderDate);

EXEC sp_executesql @InsertString,
    N'@InsOrderID INT, @InsCustID INT, @InsOrdDate DATETIME,
       @InsOrdMonth INT, @InsDelDate DATETIME',
    @PrmOrderID,
    @PrmCustomerID,
    @PrmOrderDate,
    @OrderMonth,
    @PrmDeliveryDate;
GO

sp_executesql使用此过程比用于EXECUTE执行字符串更有效。 使用时 sp_executesql ,只生成 12 个 INSERT 版本的字符串,每个表各有一个版本。 因此 EXECUTE,每个 INSERT 字符串都是唯一的,因为参数值不同。 尽管这两种方法都生成了相同的批数,但通过sp_executesql查询优化器重用执行计划的可能性更高,因此生成的字符串的相似性INSERT更大。

°C 使用 OUTPUT 参数

以下示例使用参数 OUTPUT 将语句生成的 SELECT 结果集存储在参数中 @SQLString 。 然后执行两 SELECT 个语句,以使用参数的值 OUTPUT

USE AdventureWorks2022;
GO

DECLARE @SQLString NVARCHAR(500);
DECLARE @ParmDefinition NVARCHAR(500);
DECLARE @SalesOrderNumber NVARCHAR(25);
DECLARE @IntVariable INT;

SET @SQLString = N'SELECT @SalesOrderOUT = MAX(SalesOrderNumber)
    FROM Sales.SalesOrderHeader
    WHERE CustomerID = @CustomerID';
SET @ParmDefinition = N'@CustomerID INT,
    @SalesOrderOUT NVARCHAR(25) OUTPUT';
SET @IntVariable = 22276;

EXECUTE sp_executesql @SQLString,
    @ParmDefinition,
    @CustomerID = @IntVariable,
    @SalesOrderOUT = @SalesOrderNumber OUTPUT;

-- This SELECT statement returns the value of the OUTPUT parameter.
SELECT @SalesOrderNumber;

-- This SELECT statement uses the value of the OUTPUT parameter in
-- the WHERE clause.
SELECT OrderDate,
    TotalDue
FROM Sales.SalesOrderHeader
WHERE SalesOrderNumber = @SalesOrderNumber;

示例:Azure Synapse Analytics 和 Analytics Platform System (PDW)

D. 执行 标准版LECT 语句

以下示例创建并执行包含名为 <a0SELECT/> 的嵌入参数的语句。

EXECUTE sp_executesql
    N'SELECT * FROM AdventureWorksPDW2012.dbo.DimEmployee
    WHERE EmployeeKey = @level',
    N'@level TINYINT',
    @level = 109;