如何证明以下 T-SQL 对性能不利?

oat*_*oda 4 performance sql-server stored-procedures t-sql query-performance performance-tuning

我最近继承了一个包含大量存储过程的代码库。他们支持的系统遇到了许多我正在研究的性能问题。

许多存储过程都有这样的模式:

  1. 创建临时表
  2. 建立动态 SQL 查询以插入一堆记录,例如

    DECLARE @sql VARCHAR(MAX)
    SET @sql = 'INSERT INTO @tempTable
    SELECT SomeColumn, SomeColumn2, SomeColumn3, etc FROM MyTable'
    
    IF @someParam = [SomeValue]
        SET @sql = @sql + 'WHERE SomeColumn = [SomeValue]';
    
    IF @someOtherParam = [SomeOtherValue]
        SET @sql = @sql + 'WHERE SomeOtherColum = [SomeOtherValue]';
    
    Run Code Online (Sandbox Code Playgroud)
  3. 执行这个动态sql

    EXEC(@sql);
    
    Run Code Online (Sandbox Code Playgroud)
  4. 从临时表中选择并带入一堆附加信息以返回给客户端。

    SELECT
    
       ...
    
    FROM
    
       @tempTable
       INNER JOIN ...
    
    Run Code Online (Sandbox Code Playgroud)

我的即时想法是:

  • 有动态 SQL,所以没有缓存计划,这意味着每次都会生成计划。
  • 有一个INSERT SELECT模式,所以表锁定更有可能是一个问题。

我用这种方式重写了一些存储过程:

    SELECT 

       ...

    FROM

        MyTable 
        INNER JOIN ...

    WHERE

    (
        @someParam != SomeValue
        OR
        SomeColumn = SomeValue
    )
    AND
    (
        @someOtherParam != SomeOtherValue
        OR
        SomeOtherColumn = SomeOtherValue
    )        
Run Code Online (Sandbox Code Playgroud)

通过比较 SQL Management Studio 中的执行计划和客户端统计信息,我没有加快存储过程的速度,所以我担心建议对所有存储过程进行批量重写。

我正在尝试对实时客户场景进行一些分析,但目前还无法证明我的想法。

任何人都可以证实我的想法背后的理论,或者有什么更好的方法来证明我的怀疑?

问题是我读到动态 SQL 并不总是一个封闭的案例 - 即它取决于它的使用方式。我对锁定的理解也落入了这样一个事实,即我无法 100% 确认这种类型INSERT SELECT将如何锁定表。

Rem*_*anu 17

有动态SQL,所以没有缓存计划,意味着每次都会生成计划

不一定是真的。动态 SQL 可以(并且确实)像静态 SQL 一样使用缓存计划。对于动态搜索条件,解析为动态 SQL 通常是正确的答案。有关更多详细信息,请参阅T-SQL 中的动态搜索条件

有一个 INSERT SELECT 模式,所以表锁定更有可能是一个问题。

不一定是真的,特别是 @tempTable

以这种方式重写了一些存储过程

使用这样的多个OR条件是一种反模式。您正在迫使查询优化器提出一个适用于所有这些参数的任何值的计划。通常唯一的解决方案是扫描,忽略任何索引。原始代码更好。

谁能提供......任何更好的方法来证明我的怀疑?

是的。措施。使用类似Waits 和 Queues的方法。不要依赖你的直觉。找到瓶颈并相应地解决它们。

  • 你睡莱姆斯先生吗?在此向您的贡献致敬。 (4认同)
  • 不是更新锁定开始。是一个读(`SELECT`),所以会根据你的读隔离级别锁定。表扫描 * 可能 * 升级,但这是由于扫描(即您在 SELECT 中的 WHERE 条件)。INSERT 与两者无关。 (2认同)

小智 5

其实我觉得原版效果更好。

where语句中使用“or”子句通常会显着降低性能。

而且我认为每次都重新评估动态 sql 查询是不正确的。据我所知,DBMS 将根据查询文本缓存查询计划。但是,如果您修改其中的一个空格,它将被重新评估。因此,为了优化动态查询,如果您在文本 ( @myValue) 中使用变量而不是连接它们的值(您在调用 时传递变量值)肯定会更好exec

至于临时表 - 它是一个变量,这意味着它比实际临时表(例如#table)更快/更少的资源需求。我们最近遇到了一个类似的问题,服务器需要很长时间来执行一个复杂的查询,而优化结果是首先减少结果,然后再做其余的连接,这正是他们在这里所做的。当事实证明这是问题时,首先尝试对所有表进行统计更新,但如果这不起作用,那么这实际上就是解决方案。