使用表变量时查询需要 10 分钟,而使用临时表时查询需要 2 秒

Query takes 10 minutes when table variables are used versus 2 seconds when temporary table is used

提问人:Madhukar 提问时间:9/14/2023 最后编辑:Madhukar 更新时间:9/20/2023 访问量:142

问:

有人写了一个使用表变量的旧查询,它在 2-3 分钟内运行。从过去 2 周开始,即使数据集大小大致相同,它也会永久运行。因此,我正在尝试优化脚本。当我用临时表替换表变量时,脚本的运行速度提高了 99.99%。究竟是什么原因?我同意表变量不适合大型数据集。但是,在这种情况下,记录计数以数千为单位。表变量和临时表都存储在 tempDB 中,那么为什么会有这种差异呢?在后端会发生什么,而表变量在 2-3 分钟内运行,现在需要花费大量时间?

    DECLARE @test_table1 TABLE(/*some columns*/ );

    INSERT INTO @test_table1
    SELECT /*some columns*/ FROM SomeTable1
        LEFT JOIN SomeTable2
        LEFT JOIN SomeTable3
        LEFT JOIN SomeTable4
        LEFT JOIN SomeTable5
    WHERE --some conditions with string and date functions

    UPDATE @test_table1 SET col1 = 'xyz' WHERE SUBSTRING(col2,1,4) IN ('some values')

    DECLARE @test_table2 TABLE (/*some columns*/);

    WITH [cte1] AS (
        SELECT /*some columns*/ FROM SomeTable1
            INNER JOIN @test_table1 
            LEFT JOIN SomeTable2)
    
    ,[cte2] AS(
        SELECT /*some columns*/ FROM SomeTable1
            INNER JOIN @test_table1 
            LEFT JOIN SomeTable2    
            LEFT JOIN SomeTable3
        UNION
        SELECT /*some columns*/ FROM SomeTable1
            INNER JOIN @test_table1 
            LEFT JOIN SomeTable2    
            LEFT JOIN SomeTable3)
    ,[cte3] AS(
        SELECT DISTINCT /*some columns*/ FROM [cte2]
        WHERE /*some simple conditions*/)

    INSERT INTO @test_table2 
    SELECT /*some columns with string concat using stuff and xml path*/ FROM [cte3]
    GROUP BY /*some columns*/
sql sql-server sql-server-2012

评论

6赞 Thom A 9/14/2023
在 SQL Server 2012 中,数据引擎(不幸的是)假定表变量仅包含行。由于数据引擎的估计值不佳,这可能会导致一些非常差的性能。由于您的表变量似乎包含许多行,因此如果错误的估计导致性能问题,我不会感到惊讶。
7赞 Stu 9/14/2023
如果比较变量/临时表之间的执行计划,您可能会看到问题,如前所述,这很可能是由于基数估计造成的。临时表几乎总是更可取的,尤其是在较旧的 SQL Server 版本中。
3赞 Aaron Bertrand 9/14/2023
甚至 2-3 分钟似乎......荒谬。这个查询在做什么,即使你使用什么类型的临时对象,它都需要几秒钟以上?如果消除临时对象周期会发生什么?
3赞 siggemannen 9/14/2023
错误查询是错误查询。您不必为每个查询使用每个 sql server 运算符,也不必将所有内容塞进一个语句中。最后,UNION 而不是 UNION ALL 与 DISTINCT 相结合通常表明在此过程中出现了问题。试着把整个事情简化
3赞 siggemannen 9/18/2023
如果你不显示计划或任何真正的细节,我想我宁愿投票关闭问题,@ThomA的答案可能尽可能好,临时变量的基数(行数)与 EV 不同。临时表,以便引擎选择不同的查询计划,案例关闭

答:

4赞 jean 9/19/2023 #1

“表变量保存在RAM中,使所有对它的访问都非常快 而临时表是在磁盘上物理创建的。

每当有人来问我为什么临时表实际上比表变量显示更好的性能时,我都会听到这句话的一些变化。

因此,我必须记住,即使在尝试从最简单的查询中获取数据时,引擎也会做出大量估计和假设。其中一个最重要的估计值是关于返回数据集的大小。 大多数情况下,这些表变量的不良性能是由于数据集返回了大量数据,超出了引擎愿意从 RAM 中获取的数据 并开始使用磁盘,与临时表使用的温度完全相同。

那么,为什么性能更差,性能不一样呢? 表变量应该很小,确实非常小,最重要的是,比“等效”/“默认”临时表小。 随着数据集大小的增加,引擎必须重新分配越来越多的磁盘空间,并进行越来越多的页面拆分和填充,并且由于所有这些初始假设,表变量的开销要糟糕得多。

这就是为什么我对任何尝试使用表变量或临时表的人的建议是:你有没有测试过相反的方式?

TOP 100 也可能具有误导性,这意味着只有您想显示前 100 名,但这并不意味着引擎不需要检索整个表来对一大堆数据进行排序。所有这些都是以牺牲初始预留内存空间为代价的

评论

1赞 Yair Maron 9/19/2023
这是我看到的关于这个话题的最佳答案