zoukankan      html  css  js  c++  java
  • SQLSERVER 动态执行SQL sp_executesql与EXEC

    摘自SQL server帮助文档
    对大家优查询速度有帮助!

    建议使用 sp_executesql 而不要使用 EXECUTE 语句执行字符串。支持参数替换不仅使 sp_executesql 比 EXECUTE 更通用,而且还使 sp_executesql 更有效,因为它生成的执行计划更有可能被 SQL Server 重新使用。

    自包含批处理

    sp_executesql 或 EXECUTE 语句执行字符串时,字符串被作为其自包含批处理执行。SQL Server 将Transact-SQL 语句或字符串中的语句编译进一个执行计划,该执行计划独立于包含 sp_executesql 或 EXECUTE 语句的批处理的执行计划。下列规则适用于自含的批处理:

    • 直到执行 sp_executesql 或EXECUTE 语句时才将sp_executesql 或 EXECUTE 字符串中的 Transact-SQL 语句编译进执行计划。执行字符串时才开始分析或检查其错误。执行时才对字符串中引用的名称进行解析。
    • 执行的字符串中的 Transact-SQL 语句,不能访问 sp_executesql 或 EXECUTE 语句所在批处理中声明的任何变量。包含 sp_executesql 或 EXECUTE 语句的批处理不能访问执行的字符串中定义的变量或局部游标。
    • 如果执行字符串有更改数据库上下文的 USE 语句,则对数据库上下文的更改仅持续到 sp_executesql 或 EXECUTE 语句完成。

    通过执行下列两个批处理来举例说明:

    /* Show not having access to variables from the calling batch. */
    DECLARE @CharVariable CHAR(3)
    SET @CharVariable = 'abc'
    /* sp_executesql fails because @CharVariable has gone out of scope. */
    sp_executesql N'PRINT @CharVariable'
    GO
    
    /* Show database context resetting after sp_executesql completes. */
    USE pubs
    GO
    sp_executesql N'USE Northwind'
    GO
    /* This statement fails because the database context
         has now returned to pubs. */
    SELECT * FROM Shippers
    GO
    
    替换参数值

    sp_executesql 支持对 Transact-SQL 字符串中指定的任何参数的参数值进行替换,但是 EXECUTE 语句不支持。因此,由 sp_executesql 生成的 Transact-SQL 字符串比由 EXECUTE 语句所生成的更相似。SQL Server 查询优化器可能将来自 sp_executesql 的 Transact-SQL 语句与以前所执行的语句的执行计划相匹配,以节约编译新的执行计划的开销。

    使用 EXECUTE 语句时,必须将所有参数值转换为字符或 Unicode 并使其成为 Transact-SQL 字符串的一部分:

    DECLARE @IntVariable INT
    DECLARE @SQLString NVARCHAR(500)
    /* Build and execute a string with one parameter value. */
    SET @IntVariable = 35
    SET @SQLString = N'SELECT * FROM pubs.dbo.employee WHERE job_lvl = ' +
                       CAST(@IntVariable AS NVARCHAR(10))
    EXEC(@SQLString)
    /* Build and execute a string with a second parameter value. */
    SET @IntVariable = 201
    SET @SQLString = N'SELECT * FROM pubs.dbo.employee WHERE job_lvl = ' +
                       CAST(@IntVariable AS NVARCHAR(10))
    EXEC(@SQLString)
    

    如果语句重复执行,则即使仅有的区别是为参数所提供的值不同,每次执行时也必须生成全新的 Transact-SQL 字符串。从而在下面几个方面产生额外的开销:

    • SQL Server 查询优化器具有将新的 Transact-SQL 字符串与现有的执行计划匹配的能力,此能力被字符串文本中不断更改的参数值妨碍,特别是在复杂的 Transact-SQL 语句中。
    • 每次执行时均必须重新生成整个字符串。
    • 每次执行时必须将参数值(不是字符或 Unicode 值)投影到字符或 Unicode 格式。

    sp_executesql 支持与 Transact-SQL 字符串相独立的参数值的设置:

    DECLARE @IntVariable INT
    DECLARE @SQLString NVARCHAR(500)
    DECLARE @ParmDefinition NVARCHAR(500)
    
    /* Build the SQL string once. */
    SET @SQLString =
           N'SELECT * FROM pubs.dbo.employee WHERE job_lvl = @level'
    /* Specify the parameter format once. */
    SET @ParmDefinition = N'@level tinyint'
    
    /* Execute the string with the first parameter value. */
    SET @IntVariable = 35
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
                            @level = @IntVariable
    /* Execute the same string with the second parameter value. */
    SET @IntVariable = 32
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
                            @level = @IntVariable
    

    sp_executesql 示例完成的任务与前面的 EXECUTE 示例所完成的相同,但有下列额外优点:

    • 因为 Transact-SQL 语句的实际文本在两次执行之间未改变,所以查询优化器应该能将第二次执行中的 Transact-SQL 语句与第一次执行时生成的执行计划匹配。这样,SQL Server 不必编译第二条语句。
    • Transact-SQL 字符串只生成一次。
    • 整型参数按其本身格式指定。不需要转换为 Unicode。

    说明   为了使 SQL Server 重新使用执行计划,语句字符串中的对象名称必须完全符合要求。

    重新使用执行计划

    在 SQL Server 早期的版本中要重新使用执行计划的唯一方式是,将 Transact-SQL 语句定义为存储过程然后使应用程序执行此存储过程。这就产生了管理应用程序的额外开销。使用 sp_executesql 有助于减少此开销,并使 SQL Server 得以重新使用执行计划。当要多次执行某个 Transact-SQL 语句,且唯一的变化是提供给该 Transact-SQL 语句的参数值时,可以使用 sp_executesql 来代替存储过程。因为 Transact-SQL 语句本身保持不变仅参数值变化,所以 SQL Server 查询优化器可能重复使用首次执行时所生成的执行计划。

    下例为服务器上除四个系统数据库之外的每个数据库生成并执行 DBCC CHECKDB 语句:

    USE master
    GO
    SET NOCOUNT ON
    GO
    DECLARE AllDatabases CURSOR FOR
    SELECT name FROM sysdatabases WHERE dbid > 4
    
    OPEN AllDatabases
    
    DECLARE @DBNameVar NVARCHAR(128)
    DECLARE @Statement NVARCHAR(300)
    
    FETCH NEXT FROM AllDatabases INTO @DBNameVar
    WHILE (@@FETCH_STATUS = 0)
    BEGIN
         PRINT N'CHECKING DATABASE ' + @DBNameVar
         SET @Statement = N'USE ' + @DBNameVar + CHAR(13)
            + N'DBCC CHECKDB (' + @DBNameVar + N')'
       EXEC sp_executesql @Statement
         PRINT CHAR(13) + CHAR(13)
         FETCH NEXT FROM AllDatabases INTO @DBNameVar
    END
    
    CLOSE AllDatabases
    DEALLOCATE AllDatabases
    GO
    SET NOCOUNT OFF
    GO

    当目前所执行的 Transact-SQL 语句包含绑定参数标记时,SQL Server ODBC 驱动程序使用 sp_executesql 完成 SQLExecDirect。但例外情况是 sp_executesql 不用于执行中的数据参数。这使得使用标准 ODBC 函数或使用在 ODBC 上定义的 API(如 RDO)的应用程序得以利用 sp_executesql 所提供的优势。定位于 SQL Server 2000 的现有的 ODBC 应用程序不需要重写就可以自动获得性能增益。有关更多信息,请参见使用语句参数

    用于 SQL Server 的 Microsoft OLE DB 提供程序也使用 sp_executesql 直接执行带有绑定参数的语句。使用 OLE DB 或 ADO 的应用程序不必重写就可以获得 sp_executesql 所提供的优势。

    declare @user varchar(1000)declare @moTable varchar(20)select @moTable = 'MT_10'declare @sql nvarchar(4000)  --定义变量,注意类型set @sql='select @user = count(distinct userid)  from '+@moTable  --为变量赋值--执行@sql中的语句exec sp_executesql @sql  ,N'@user varchar(1000) out'  --表示@sql中的语句包含了一个输出参数  ,@user out                   --和调用存储过程差不多,指定输出参数值print @user

     

     

    sp_executesql语法

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

    参数
    [@stmt =] stmt

    包含 Transact-SQL 语句或批处理的 Unicode 字符串,stmt 必须是可以隐式转换为 ntext 的 Unicode 常量或变量。不允许使用更复杂的 Unicode 表达式(例如使用 + 运算符串联两个字符串)。不允许使用字符常量。如果指定常量,则必须使用 N 作为前缀。例如,Unicode 常量 N'sp_who' 是有效的,但是字符常量 'sp_who' 则无效。字符串的大小仅受可用数据库服务器内存限制。

    stmt 可以包含与变量名形式相同的参数,例如:

    N'SELECT * FROM Employees WHERE EmployeeID = @IDParameter'
    stmt 中包含的每个参数在 @params 参数定义列表和参数值列表中均必须有对应项。

    [@params =] N'@parameter_name  data_type [,...n]'

    字符串,其中包含已嵌入到 stmt 中的所有参数的定义。该字符串必须是可以隐式转换为 ntext 的 Unicode 常量或变量。每个参数定义均由参数名和数据类型组成。n 是表明附加参数定义的占位符。stmt 中指定的每个参数都必须在 @params 中定义。如果 stmt 中的 Transact-SQL 语句或批处理不包含参数,则不需要 @params。该参数的默认值为 NULL。

    [@param1 =] 'value1'

    参数字符串中定义的第一个参数的值。该值可以是常量或变量。必须为 stmt 中包含的每个参数提供参数值。如果 stmt 中包含的 Transact-SQL 语句或批处理没有参数,则不需要值。

    n

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

    返回代码值
    0(成功)或 1(失败)

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

    注释
    在批处理、名称作用域和数据库上下文方面,sp_executesql 与 EXECUTE 的行为相同。sp_executesql stmt 参数中的 Transact-SQL 语句或批处理在执行 sp_executesql 语句时才编译。然后编译 stmt 中的内容并作为执行计划运行(独立于名为 sp_executesql 的批处理的执行计划)。sp_executesql 批处理不能引用调用 sp_executesql 的批处理中声明的变量。sp_executesql 批处理中的本地游标和变量对调用 sp_executesql 的批处理是不可见的。对数据库上下文所作的更改只在 sp_executesql 语句结束前有效。

    如果只更改了语句中的参数值,则 sp_executesql 可用来代替存储过程多次执行 Transact-SQL 语句。因为 Transact-SQL 语句本身保持不变仅参数值变化,所以 Microsoft® 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 * FROM pubs.dbo.employee WHERE job_lvl = @level'
    SET @ParmDefinition = N'@level tinyint'
    /* Execute the string with the first parameter value. */
    SET @IntVariable = 35
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
                          @level = @IntVariable
    /* Execute the same string with the second parameter value. */
    SET @IntVariable = 32
    EXECUTE sp_executesql @SQLString, @ParmDefinition,
                          @level = @IntVariable
    替换 sp_executesql 中的参数的能力,与使用 EXECUTE 语句执行字符串相比,有下列优点:

    因为在 sp_executesql 中,Transact-SQL 语句的实际文本在两次执行之间未改变,所以查询优化器应该能将第二次执行中的 Transact-SQL 语句与第一次执行时生成的执行计划匹配。这样,SQL Server 不必编译第二条语句。


    Transact-SQL 字符串只生成一次。


    整型参数按其本身格式指定。不需要转换为 Unicode。
    权限
    执行权限默认授予 public 角色。

    示例
    A. 执行简单的 SELECT 语句
    下面的示例创建并执行一个简单的 SELECT 语句,其中包含名为 @level 的嵌入参数。

    execute sp_executesql
              N'select * from pubs.dbo.employee where job_lvl = @level',
              N'@level tinyint',
              @level = 35
    B. 执行动态生成的字符串
    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 字符串,每个月的表 1 个。使用 EXECUTE 时,因为参数值不同,每个 INSERT 字符串均是唯一的。尽管两种方法生成的批处理数相同,但因为 sp_executesql 生成的 INSERT 字符串相似,所以查询优化程序更有可能反复使用执行计划。

    ---------------------------------------------------------------------------------------------

    其它Exec和sp_executesql 使用比较


    1 :普通SQL语句可以用Exec执行

    eg:   Select * from tableName
             Exec('select * from tableName')
             Exec sp_executesql N'select * from tableName'    -- 请注意字符串前一定要加N

    2:字段名,表名,数据库名之类作为变量时,必须用动态SQL

    eg:  
    declare @fname varchar(20)
    set @fname = 'FiledName'
    Select @fname from tableName              -- 错误,不会提示错误,但结果为固定值FiledName,并非所要。
    Exec('select ' + @fname + ' from tableName')     -- 请注意 加号前后的 单引号的边上加空格

    当然将字符串改成变量的形式也可
    declare @fname varchar(20)
    set @fname = 'FiledName' --设置字段名

    declare @s varchar(1000)
    set @s = 'select ' + @fname + ' from tableName'
    Exec(@s)                -- 成功
    exec sp_executesql @s   -- 此句会报错

    declare @s Nvarchar(1000)  -- 注意此处改为nvarchar(1000)
    set @s = 'select ' + @fname + ' from tableName'
    Exec(@s)                -- 成功    
    exec sp_executesql @s   -- 此句正确

    3. 输出参数
    declare @num int,
            @sqls nvarchar(4000)
    set @sqls='select count(*) from tableName'
    exec(@sqls)
    --如何将exec执行结果放入变量中?

    declare @num int,
                   @sqls nvarchar(4000)
    set @sqls='select @a=count(*) from tableName '
    exec sp_executesql @sqls,N'@a int ,@num
    select @num 

    ---实际例子讲解:declare @user varchar(1000)
    declare @moTable varchar(20)

    select @moTable = 'MT_10'




    declare @sql nvarchar(4000)  --定义变量,注意类型


    set @sql='select @user = count(distinct userid)  from '+@moTable  --为变量赋值

    --执行@sql中的语句
    exec sp_executesql @sql
      ,N'@user varchar(1000) out'  --表示@sql中的语句包含了一个输出参数
      ,@user out                   --和调用存储过程差不多,指定输出参数值

    print @user

     本例中,@moTable 为嵌入参数。

    作者:wpf之家
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Codeforces 992C(数学)
    Codeforces 990C (思维)
    Codeforces 989C (构造)
    POJ 1511 Invitation Cards(链式前向星,dij,反向建边)
    Codeforces 1335E2 Three Blocks Palindrome (hard version)(暴力)
    POJ 3273 Monthly Expense(二分)
    POJ 2566 Bound Found(尺取前缀和)
    POJ 1321 棋盘问题(dfs)
    HDU 1506 Largest Rectangle in a Histogram(单调栈)
    POJ 2823 Sliding Window(单调队列)
  • 原文地址:https://www.cnblogs.com/wpf123/p/2347411.html
Copyright © 2011-2022 走看看