为啥将 WHERE 子句放在视图之外的性能很差

Posted

技术标签:

【中文标题】为啥将 WHERE 子句放在视图之外的性能很差【英文标题】:Why does putting a WHERE clause outside view have terrible performance为什么将 WHERE 子句放在视图之外的性能很差 【发布时间】:2011-07-11 17:28:19 【问题描述】:

假设你有一个观点:

CREATE VIEW dbo.v_SomeJoinedTables AS
SELECT
    a.date,
    a.Col1,
    b.Col2,
    DENSE_RANK() 
      OVER(PARTITION BY a.date, a.Col2 ORDER BY a.Col3) as Something
FROM a JOIN b on a.date = b.date

我发现:

SELECT *
FROM v_SomeJoinedTables
WHERE date > '2011-01-01'

差很多
SELECT *, 
   DENSE_RANK() 
     OVER(PARTITION BY a.date, a.Col2 ORDER BY a.Col3) as Something
FROM a JOIN b ON a.date = b.date
WHERE a.date > '2011-01-01'

我很惊讶这两条语句的查询计划不一样。

我也尝试过使用内联表值函数,但查询仍然比我复制和粘贴视图逻辑的代码长 100-1000 倍。

有什么想法吗?

【问题讨论】:

那么查询计划是什么样的?您是否缺少索引?视图是否返回太多行?如果 where 子句被应用在错误的地方? 你的视图是否恰好在调用视图? 如果您的视图不包含窗口函数会怎样?计算整个集合排名的 VIEW 的预期语义结果是什么,但是对视图的查询添加了一个新条件——应该在外部条件之前还是之后应用 RANK insde?​​span> 【参考方案1】:

它被称为“Predicate pushing”,也就是延迟过滤。

SQL Server 并不总是意识到 WHERE 可以“更早”地在视图内有效地应用。

在 SQL Server 2008 中已得到缓解,可以按预期工作

【讨论】:

很好的参考。这真的很可怕。它证实了我的怀疑,即 SQL Server 从未对 SQL 中的 TOP 关键字的概念感到满意。编写优化查询真的不需要知道这些东西。 当我发现 SQL Server 2005 处理 RANK 函数有多糟糕时,我感到非常惊讶。确实应该有一个修补程序,并且不需要升级到 SQL 2008。 @bpeikes:是的,请参阅***.com/q/4230838/27535 并在此处发表评论:***.com/q/2798094/27535 不确定这个。我遇到了与 SQL Server 2008 R2 几乎相同的场景的问题。【参考方案2】:

我不是 SQL 专家,所以我可能会因为自己的愚蠢而被否决,但我的猜测是,在第一种情况下,SQL 会在应用谓词之前获取 整个 视图的结果在 WHERE 子句中。因此,当您查询视图时,它会选择所有记录,将它们放入内存中,然后在完成后应用日期过滤器。

这似乎类似于在 WHERE 中应用过滤器之前获取连接中指定的整个数据集的方式(这里的教训是,您应该尽可能在 ON 子句中应用谓词)。

除非视图以某种方式被区别对待。

【讨论】:

+1 我认为你是绝对正确的。也许统计信息已关闭,您必须通过比较每个查询的估计/实际计划来进行验证,但视图中的查询很可能在应用过滤器之前实现了整个连接,而外部查询可以使用过滤器如果可以更早地从连接的一侧消除行,则选择更有效的连接类型。 视图只是完整表达式的简写。如果它们不是同样可优化的,它们就没有多大用处。 awesome....我们在这里很少使用视图,所以我没有理由深入研究它们。很高兴知道它背后有一些解释......更好地知道它在 2008 年会更好【参考方案3】:

OVER() 语法在 SS2005 中是全新的,显然没有很好地集成到优化器中。我建议你尝试一个更传统的表达方式?如果您关心可优化性,可能不是表达式。

http://www.sqlteam.com/article/sql-sever-2005-using-over-with-aggregate-functions

或者,更好的是,更加熟悉分析器 - 视图应该是可修复的。

【讨论】:

仅供参考,我的链接表明这已解决。也看到这个(cmets 太)***.com/q/2798094/27535 也许,但也许不是他的网站/版本。当实现如此狡猾以至于您需要了解您或我所知道的一切才能可靠地工作时,这并不是很好。 确实如此。而像你我这样的人往往不使用视图,所以不会每天看到它......【参考方案4】:

从技术上讲,您不是在比较相同的 SQL 语句。您的视图表明它返回 a.date, a.Col1, b.Col2, 加上您的 DENSE_RANK() 函数。在没有视图的查询中,您返回所有列。

起初,您可能认为返回所有列会更糟。但是如果不知道包括索引在内的表结构是什么样子,就很难确定哪个更好。

您是否比较了每条语句的查询计划?

【讨论】:

几乎有效...视图较慢,这就是问题的重点。即使内联查询被 SELECT * “残障”【参考方案5】:

作为一种解决方法,我建议使用函数而不是视图,以便您可以传入数据参数。

【讨论】:

以上是关于为啥将 WHERE 子句放在视图之外的性能很差的主要内容,如果未能解决你的问题,请参考以下文章

查询索引视图时的 WHERE 子句性能

where 子句中使用的 SQL Server 查询性能视图

在 where 子句中使用 DATEADD 的 TSQL 性能问题

sql server查询性能where子句

Oracle查询性能优化

带有 Node.JS 的 MongoDB:$where 子句性能