物化视图和查询计划中的分组依据

Joh*_*ohn 1 sql-server materialized-view

部分出于好奇,我想知道是否可以使用索引(物化)视图来加速对某个基表的计数查询。

查询类似于

SELECT COUNT(*)
FROM BaseTable
WHERE Slot = ?;
Run Code Online (Sandbox Code Playgroud)

所以我创建了一个视图

CREATE VIEW IndexedView
WITH SCHEMABINDING AS
SELECT bt.Slot, COUNT_BIG(*) AS COUNT
FROM dbo.BaseTable bt
GROUP BY bt.Slot;
Run Code Online (Sandbox Code Playgroud)

有聚集索引

CREATE UNIQUE CLUSTERED INDEX IX_Main
ON IndexedView (Slot);
Run Code Online (Sandbox Code Playgroud)

这有效,我现在可以将原始查询编写为

SELECT COUNT
FROM IndexedView
WHERE Slot = ?
Run Code Online (Sandbox Code Playgroud)

并更快地获得所需的结果。

唉,这对我来说没什么用,因为我的查询通常不是手工制作的。我真的需要通过使用索引视图作为某种索引来使原始查询变得更快BaseTable- 我想我在某处读到这可能在某些情况下发生,但根据我的测试,而不是在这个测试中。

所以我的问题是:

  • 在这种情况下,索引视图还能以某种方式帮助我吗?
  • 任何人都可以推荐来源/文献而不是解释在哪些情况下索引视图确实用于优化它们所基于的表的查询?

编辑:关于重复的问题 - 我对 GROUP BY 和索引视图的聚合方面更感兴趣。答案帮助我找到了我犯的一个愚蠢的错误,现在它也对我有用。

JOYOUS ADDENDUM:现在我让它工作了,我成功地测试了它甚至可以在查询包含左联接的情况下工作,而在这些情况下,左联接实际上可以被优化掉(即 on-clause 涵盖了一个唯一索引在连接表中)。

这真的很棒,因为这意味着即使在带有左连接的查询的情况下,也可以以这样一种方式设计模式,即快速获取所有内容或特定分组的总行数。

wBo*_*Bob 6

该功能称为“索引视图匹配”,要正确使用可能很棘手。此处概述了许多先决条件:解析视图上的索引 加上索引视图需要能够回答您的查询所提出的问题。我希望它可以在开发人员/企业版中用于简单的场景,并且我让它可以使用简单的架构和查询:

-- Indexed view matching
USE tempdb
GO

IF OBJECT_ID('dbo.BaseTable') IS NOT NULL DROP TABLE dbo.BaseTable
GO

CREATE TABLE dbo.BaseTable (

    rowId       INT IDENTITY PRIMARY KEY,

    slot        INT NOT NULL,
    someDate    DATETIME DEFAULT GETDATE(),
    someData    UNIQUEIDENTIFIER DEFAULT NEWID(),
)
GO


-- Add dummy data
;WITH cte AS (
SELECT TOP 1000000 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.BaseTable ( slot, someDate, someData )
SELECT
    rn % 999,
    DATEADD( day, rn % 333, '1 Jan 2016' ), 
    NEWID()
FROM cte

CHECKPOINT
GO --10


DECLARE @slot INT = 42

SELECT COUNT(*)
FROM dbo.BaseTable
WHERE Slot = @slot
OPTION ( RECOMPILE );
GO

CREATE VIEW dbo.IndexedView
WITH SCHEMABINDING AS
SELECT bt.Slot, COUNT_BIG(*) AS COUNT
FROM dbo.BaseTable bt
GROUP BY bt.Slot;
GO

CREATE UNIQUE CLUSTERED INDEX IX_Main
ON IndexedView (Slot);
GO


-- Indexed view matching
DECLARE @slot INT = 42

SELECT COUNT(*)
FROM dbo.BaseTable
WHERE Slot = @slot
OPTION ( RECOMPILE );
GO


-- Indexed view not used as it can't 'cover' the query
DECLARE @slot INT = 42

SELECT COUNT(*)
FROM dbo.BaseTable
WHERE Slot = @slot
AND rowId = 42
OPTION ( RECOMPILE );
GO


-- Query Indexed view directly with NOEXPAND hint
DECLARE @slot INT = 42

SELECT COUNT(*)
FROM dbo.IndexedView WITH ( NOEXPAND )
WHERE Slot = @slot
OPTION ( RECOMPILE );
GO


-- EXPAND VIEWS hint ensures Indexed view is NOT used.
DECLARE @slot INT = 42

SELECT COUNT(*)
FROM dbo.IndexedView 
WHERE Slot = @slot
OPTION ( RECOMPILE, EXPAND VIEWS );
GO
Run Code Online (Sandbox Code Playgroud)

执行计划:

索引视图匹配

在我上面的测试脚本中还有一些其他查询,您可以看到索引视图未用于其中一些。

以下是查询优化器的架构师之一 Conor Cunningham 对索引视图的引用:

查询优化器包含在原始查询文本显式引用视图的情况下以及用户提交使用与视图相同的组件(以任何等效顺序)的查询的情况下使用此索引的逻辑。

但 ...

[复杂性] ...使查询优化器难以考虑首先执行视图评估,然后处理查询的其余部分的计划。任意树匹配是一个计算复杂的问题,视图的特征集太大而无法有效地执行此操作。

“Microsoft SQL Server 2008 Internals”,第 8 章:查询优化器 - Conor Cunningham

...索引视图适用于基本 SPJG (SELECT-PROJECT-JOIN-GROUPBY) 查询。联接必须是内部联接,而不是自联接,并且必须是键联接。每个运营商都有一系列的限制...

来源

我认为这些报价很有说服力。您可能还希望考虑过滤索引。