我有一个简单的索引视图。当我查询它时,它很慢。首先,我向您展示架构和索引。然后是简单的查询。最后一个查询计划screnie。
这就是它的样子: -
CREATE view [dbo].[PostsCleanSubjectView] with SCHEMABINDING AS
SELECT PostId, PostTypeId,
[dbo].[ToUriCleanText]([Subject]) AS CleanedSubject
FROM [dbo].[Posts]
我的udf ToUriCleanText
只是用空字符替换各种字符。例如。用''替换所有'#'字符。
然后我在这上面添加了两个索引: -
主键索引(即聚集索引)
CREATE UNIQUE CLUSTERED INDEX [PK_PostCleanSubjectView] ON
[dbo].[PostsCleanSubjectView]
(
[PostId] 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
非集群指数
CREATE NONCLUSTERED INDEX [IX_PostCleanSubjectView_PostTypeId_Subject] ON
[dbo].[PostsCleanSubjectView]
(
[CleanedSubject] ASC,
[PostTypeId] 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
现在,它有大约25K行。什么都没有。
当我进行以下查询时,它们都需要大约4几秒钟。 WTF?这应该是..基本上是即时的!
SELECT a.PostId
FROM PostsCleanSubjectView a
WHERE a.CleanedSubject = 'Just-out-of-town'
SELECT a.PostId
FROM PostsCleanSubjectView a
WHERE a.CleanedSubject = 'Just-out-of-town' AND a.PostTypeId = 1
我做错了什么? UDF搞砸了吗?我想,因为我已经为这个视图编制了索引,所以它将被实现。因此,它不必计算该字符串列。
这是查询计划的一个屏幕,如果这有帮助: -
另外,请注意它正在使用的索引?为什么使用该索引?
该指数是......
CREATE NONCLUSTERED INDEX [IX_Posts_PostTypeId_Subject] ON [dbo].[Posts]
(
[PostTypeId] ASC,
[Subject] 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
所以,是的,任何想法的人?
CREATE FUNCTION [dbo].[ToUriCleanText]
(
@Subject NVARCHAR(300)
)
RETURNS NVARCHAR(350) WITH SCHEMABINDING
AS
BEGIN
<snip>
// Nothing insteresting in here.
//Just lots of SET @foo = REPLACE(@foo, '$', ''), etc.
END
是的,这是因为我没有在视图上使用索引,必须手动确保我没有展开视图。该服务器是Sql Server 2008标准版。完整的答案如下。
这是证明,WITH (NOEXPAND)
谢谢大家帮我解决这个问题:)
答案 0 :(得分:18)
什么版本的SQL Server?我相信只有Enterprise和Developer Edition会自动使用索引视图,而其他人则使用查询提示来支持它。
SELECT a.PostId
FROM PostsCleanSubjectView a WITH (NOEXPAND)
WHERE a.CleanedSubject = 'Just-out-of-town' AND a.PostTypeId = 1
来自Query Hints (Transact SQL) on MSDN:
仅当在查询的SELECT部分中直接引用视图并且指定了WITH(NOEXPAND)或WITH(NOEXPAND,INDEX(index_value [,... n]))时,才会展开索引视图。
答案 1 :(得分:4)
我在执行计划中的查询代码中看到@符号。有一个字符串变量。
如果字符串变量的类型与索引中字符串列的类型不匹配,则SQL Server具有NASTY行为。 Sql Server将... 将整个列转换为该类型,执行快速查找,然后丢弃已转换的索引,以便它可以在下次查询时再次执行整个操作。
西蒙想出来了 - 但这里有更多有用的细节:http://msdn.microsoft.com/en-us/library/ms187373.aspx
如果查询包含对索引视图和基表中都存在的列的引用,并且查询优化器确定使用索引视图提供执行查询的最佳方法,则查询优化器将使用视图上的索引。此功能称为索引视图匹配,仅在SQL Server Enterprise和Developer版本中受支持。
但是,为了使优化器考虑使用索引视图进行匹配或使用NOEXPAND提示引用的索引视图,必须将以下SET选项设置为ON:
所以,这里发生的是索引视图匹配无法正常工作。确保您使用的是Sql Server的Enterprise或Developer版本(很可能)。然后根据文章检查您的SET选项。
答案 2 :(得分:0)
我最近构建了一个包含数亿个调用详细记录的大型数据库,并且我在查询和视图中使用了一些函数,并将其转换为持久计算列。这样做得更好,因为我可以对计算列进行索引。
我没有使用SQL Enterprise,所以我没有机会使用索引视图。索引视图是否应该能够索引UDF的确定性结果?
答案 3 :(得分:0)
我怀疑它必须在每行调用该函数才能在where子句中进行比较。我将公开主题,直接运行查询检查,看看时间是如何运作的。每当我使用函数修改值然后在where子句中使用它时,我一般都会看到很多缓慢...
答案 4 :(得分:0)
使用索引视图可以获得哪些好处?是否无法正确索引表格本身?如果没有充分的理由,您就会增加复杂性并要求优化器以更低的灵活性处理更多的数据库对象。
您是否使用标准索引评估了相同的查询逻辑?
混合使用UDF逻辑会使事情更加混乱。
答案 5 :(得分:0)
如果您只想保留UDF的返回值,请考虑持久计算列而不是索引视图。
答案 6 :(得分:0)
对于某些数据访问层,例如EF Core,很难添加NOEXPAND-因此,您可以创建一个附加视图(未绑定模式)并将其添加到那里。您可以随心所欲地对其进行命名,但是我想在名称中明确添加NOEXPAND作为提醒。
CREATE VIEW [dbo].[DailySummary_NOEXPAND]
AS
SELECT Col1, Col2, Col3 FROM [dbo].[DailySummary] WITH (NOEXPAND)
END
也在Azure SQL(标准层)上进行了测试
重要提示:您可以使用SELECT *
,但是如果基础视图发生更改,则列索引可能会不同步,并且实际上您可能会获得错误的数据。比specify columns by name更安全。