SQL 服务器浏览量 |内联视图扩展指南

SQL Server Views | Inline View Expansion Guidelines

背景

大家好!

我最近了解到,在较新版本的 SQL 服务器中,查询优化器可以“扩展”SQL 视图并利用内联性能优势。这可能会对我创建何种类型的数据库对象以及创建它们的原因和时间产生一些重大影响,具体取决于何时实现这种增强的性能以及何时不实现。

例如,我不会费心为一个非常大的事务 table(性能非常重要)创建一个带有开始日期参数和结束日期参数的参数化内联 table 值函数当我可以创建一个视图并在调用查询的底部添加一个 WHERE 语句时,比如

SELECT
     Column1
FROM vw_Simple
WHERE
     Column1 BETWEEN @SomeStartDate AND @SomeEndDate

并相信查询优化器会“扩展”视图并为我提供出色的执行计划。

注意:我说的是一个简单的、非嵌套的、非索引的 SQL 服务器视图。像

CREATE VIEW vw_Simple
AS
SELECT
    Column1
    ,Column2
FROM TableA

问题

我的问题是:了解查询优化器何时可以“扩展”SQL 视图以及何时不能的确切 准则是什么?

我在 Microsoft 官方文档中找不到这个答案。

到目前为止我发现了什么

查询优化器可以扩展视图的情况:

查询优化器无法扩展视图的情况:

灰色区域

您不会在文档中找到此信息,因为它本身并不是一个单一的功能,它只是compiler/optimizer 在不同阶段通过查询工作,使用多种不同的技术来获得最佳执行计划。有时它可以安全地推送谓词,有时则不能。

请注意,“扩大视野” 在这里是错误的术语。视图总是扩展到它的定义中(NOEXPAND 除外)。你指的是所谓的 predicate pushdown.


编译期间视图会发生什么?

I've assumed here that indexed views and NOEXPAND are not being used.

当您执行查询时,编译器首先将查询解析并词法化为基本执行计划。这是一个非常粗糙、未优化的版本,它几乎反映了所写的查询。

当查询中有视图时,编译器会取出视图的预解析执行树,并把它推到执行计划中,同样是一个非常粗略的草稿。

对于派生表、CTE、相关和非相关子查询以及内联 TVF,同样的事情也会发生,只是还需要解析。

在这一点之后,你可以假设一个视图也可以被写成一个 CTE,这没有区别。

优化器可以推送视图吗?

编译器有很多技巧,谓词下推就是其中之一,简化视图也是如此。

此处编译器的能力主要取决于它是否可以推断出简化是允许,而不是可能。

例如这个查询

SELECT SomeCol
FROM (
    SELECT TOP 100 PERCENT *
    FROM (
        SELECT SomeCol, OtherCol, 1 / 0 AS ThisDoesntError
        FROM table1
    ) t
    WHERE OtherCol = 1
    ORDER BY ThisDoesntError
) t
WHERE OtherCol <> 2

对此进行优化非常简单

SELECT SomeCol
FROM table1
WHERE OtherCol = 1

因为已知 TOP 100 PERCENT... ORDER BY... 对外部查询没有影响,因此可以删除,然后是整个 ThisDoesntError 列。

那什么时候不行呢?

当优化器无法推送视图时,问题就开始了,因为它可能会更改查询的语义(并因此更改结果)。

SELECT SomeCol
FROM (
    SELECT TOP 10 *
    FROM (
        SELECT SomeCol, OtherCol, 1 / 0 AS ThisDOESError
        FROM table1
    ) t
    ORDER BY ThisDOESError
) t
WHERE OtherCol = 1

因为TOP需要根据ORDER BY ThisDOESError子句进行计算,所以不能省略ThisDOESError列,无法推过OtherCol上的过滤器.

同样这个也无法优化

SELECT SomeCol
FROM (
    SELECT SomeCol, OtherCol,
        ROW_NUMBER() OVER (PARTITION BY SomeCol ORDER BY ThirdCol) AS rn
    FROM table1
) t
WHERE rn = 1 AND OtherCol = 1

在这种情况下,因为必须在整个集合上计算行号,所以过滤器 OtherCol = 5 无法安全地通过。


有趣的是, 这个版本应该可以安全地通过(虽然没有承诺!)

SELECT SomeCol
FROM (
    SELECT SomeCol, OtherCol,
        ROW_NUMBER() OVER (PARTITION BY SomeCol ORDER BY ThirdCol) AS rn
    FROM table1
) t
WHERE rn = 1 AND SomeCol = 'Something'

在这种情况下,优化器理论上应该能够看到过滤列也是分区列,因此行号计算不会改变。