为啥Sql Indexed View总是使用聚集索引
Posted
技术标签:
【中文标题】为啥Sql Indexed View总是使用聚集索引【英文标题】:Why Sql Indexed View always use Clustered Index为什么Sql Indexed View总是使用聚集索引 【发布时间】:2010-12-16 13:01:12 【问题描述】:我需要一些关于如何调试以下问题的指针。
环境:SQL Server 2005 Enterprise。
我有一个包含聚集索引和多个非唯一非聚集索引的索引视图。但是,当我执行查询时,SQL Server 总是在我的键上执行聚集索引扫描而不是索引搜索。
这是一个简化版。
CREATE VIEW MyIndexedView WITH SCHEMABINDING
SELECT a.Col1, b.Col2, c.Col3, d.Col4
FROM a JOIN b on a.id = b.id
JOIN c on a.id = c.id
JION d on c.id = d.id
Col1 上有一个聚集索引,Col2、Col3 上有非唯一、非聚集索引。
当我运行以下查询时
SELECT a.Col1, b.Col2, c.Col3 FROM MyIndexedView WITH(NOEXPAND) WHERE b.Col2='blah'
查看执行计划,我看到 SQL 服务器在 a.Col1 上运行聚集索引扫描,而不是在 Col2 上执行索引查找。
我尝试重新创建视图和索引。
更新: 我做了一些额外的测试并在查询分析器中并排运行这两个查询。
a) SELECT a.Col1, b.Col2, c.Col3
FROM MyIndexedView WITH(NOEXPAND) WHERE b.Col2='blah'
b) SELECT a.Col1, b.Col2, c.Col3
FROM MyIndexedView WHERE b.Col2 = 'blah'
查询“a”将占用 95% 的时间并使用集群索引扫描。查询“b”只需要 5% 的时间并在 col2 上使用 Index Seek。我尝试交换查询的顺序(先运行 b 再运行)产生相同的百分比。
这个小实验证实,如果 sql 使用索引搜索,它会比集群索引扫描更快。 其次,如果我不包含“WITH(NOEXPAND)”,那么 SQL Server 将不会在索引视图上使用索引。 (也许我应该就创建索引视图的确切步骤提出另一个问题)。【问题讨论】:
如果查询分析器得出聚集索引扫描比查找更快的结论,它会进行扫描——即使使用索引。这可能与视图中的行数、索引的选择性以及相当多的其他元素有关。仅仅因为存在索引扫描并不意味着索引没有“工作” - 对于那个查询,执行索引扫描可能会更容易。 marc_s - 查看我的更新,我并排运行两个查询,如果 sql 使用索引搜索,它将比集群索引扫描快得多。 【参考方案1】:我复制了您的示例,并通过 Col2 上的索引搜索得出了预期的结果。我能够让它进行聚集索引扫描的唯一方法是禁用索引。所以首先尝试在 Col2 上重建索引以确保它确实被启用(或选中索引属性 - 选项中的“使用索引”复选框)。
这是我用来创建表、视图和索引的脚本
CREATE TABLE [dbo].[a](
[id] [int] IDENTITY(1,1) NOT NULL,
[Col1] [varchar](100) NOT NULL,
CONSTRAINT [PK_a] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[b](
[id] [int] IDENTITY(1,1) NOT NULL,
[Col2] [varchar](100) NOT NULL,
CONSTRAINT [PK_b] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[c](
[id] [int] IDENTITY(1,1) NOT NULL,
[Col3] [varchar](100) NOT NULL,
CONSTRAINT [PK_c] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[d](
[id] [int] IDENTITY(1,1) NOT NULL,
[Col4] [varchar](100) NOT NULL,
CONSTRAINT [PK_d] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE VIEW [dbo].[MyIndexedView] WITH SCHEMABINDING
AS
SELECT a.Col1, b.Col2, c.Col3, d.Col4
FROM dbo.a JOIN dbo.b on a.id = b.id
JOIN dbo.c on a.id = c.id
JOIN dbo.d on c.id = d.id
GO
/****** Object: Index [IX] Script Date: 11/13/2009 21:50:01 ******/
CREATE UNIQUE CLUSTERED INDEX [IX] ON [dbo].[MyIndexedView]
(
[Col1] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object: Index [IX2] Script Date: 11/13/2009 21:50:39 ******/
CREATE NONCLUSTERED INDEX [IX2] ON [dbo].[MyIndexedView]
(
[Col2] ASC,
[Col3] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
我这样填充表格:
declare @x int
SET @x = 0
while @x < 10
begin
INSERT INTO a (Col1 ) VALUES (newid())
INSERT INTO b (Col2 ) VALUES (newid())
INSERT INTO c (Col3 ) VALUES (newid())
INSERT INTO d (Col4 ) VALUES (newid())
SET @x=@x+1
end
执行您的查询
从 MyIndexedView WITH(NOEXPAND) WHERE Col2='blah' 中选择 Col1、Col2、Col3
显示 IX2 上的索引查找
但如果我禁用该索引 ALTER INDEX [IX2] ON [dbo].[MyIndexedView] 禁用
然后重新运行,我在 MyIndexedView.IX 上看到聚集索引扫描
【讨论】:
感谢您尝试重现该问题。我确实验证了 col2 列上的“使用索引”检查。而且我确实删除并重新创建了索引(不确定它是否与重建相同)。【参考方案2】:您的视图中有多少条记录?
如果连接的结果很小,那么扫描聚集索引比寻找另一个更经济有效。
【讨论】:
以上是关于为啥Sql Indexed View总是使用聚集索引的主要内容,如果未能解决你的问题,请参考以下文章
为啥 SQL Server 查询优化器有时会忽略明显的聚集主键?