SQL 2005索引视图的速度缓慢

时间:2010-03-10 19:41:50

标签: sql sql-server sql-server-2005 indexed-view

假设我有一个非常长的表(约3500万行),名为TimeCard,只有5列(tableID,CompanyID,UserID,ProjectID,DailyHoursWorked,entryDate)。这是一个非常直接的表格,记录员工每个公司每个项目每天的工作时间。

我现在需要生成一份报告,以了解员工每个项目每个月的总工作时数。我希望构建一个类似于表的数据结构,而不是执行报表运行时所需的聚合,我希望按月汇总所有公司/项目/用户数据,因此当报表运行时,我可以直接查询该数据结构没有执行任何运行时聚合,因为~3500万条记录可能需要几分钟。

所以我有两种不同的方式。一个创建一个额外的物理表(CompanyID,UserID,ProjectID,MonthlyHoursWorked,Month)作为我的列,只需在TimeCard表中使用trigger来修改额外表中的值。或者我可以创建索引视图。所以我试过了两个。我首先使用以下代码尝试了索引视图:

CREATE VIEW [dbo].[vw_myView] WITH SCHEMABINDING AS
SELECT 
 JobID,
 ProjectID,
 Sum(DailyHoursWorked) AS MonthTotal,
 DATEADD( Month, DATEDIFF( Month, 0, entryDate), 0 ) AS entryMonth,
 CompanyID,
 COUNT_BIG(*) AS Counter
FROM
 dbo.TimeCard 
Group By DATEADD( Month, DATEDIFF( Month, 0, entryDate ), 0 ), JobID, ProjectID, CompanyID

Go
CREATE UNIQUE CLUSTERED INDEX [IX_someIndex] ON [dbo].[vw_myView] 
(
 [CompanyID] ASC,
 [entryMonth] ASC,
 [UserID] ASC,
 [ProjectID] ASC
)

正确创建索引视图,总计约500万行。

但是,每次如果我清除SQL缓存,并运行以下查询: * select * from vw_myView where companyID = 1 * ,则需要将近3分钟。如果我按照上面提到的那样使用额外的表路由,清除了我的缓存,大约需要4秒钟。

我的问题是,索引查看这个特定场景的错误选择吗?特别是我有兴趣知道每次更改基础表(TimeCard)或对其运行查询时是否重新计算/重新聚合整个索引视图?

谢谢!

6 个答案:

答案 0 :(得分:2)

如果您未使用 Enterprise Developer 版本,则需要使用with (noexpand)提示:

select * 
from vw_myView with (noexpand)
where companyID = 1

当基础数据发生更改时,视图将仅更新与已更改数据相关的行,而不是整个表。这可能会对具有高度插入的OLTP数据库产生负面影响,但如果使用率适中,则不应造成性能问题。

A tip from Microsoft

  

作为一般性建议,任何   修改或更新视图   或视图下的基表   如果是,应该分批进行   可能,而不是单身   操作。这可能会减少一些   视图维护中的开销。

答案 1 :(得分:1)

我认为你使用索引视图是正确的道路。但是,您是否将索引放在要查询的表上,TimeCard表示您的聚合列。您需要制作JobID, ProjectID, entryDate, CompanyID索引(1索引)。如果为每列使用1个索引,它将无法解决您的问题,因为Query必须同时使用所有4个索引。

我认为使用触发器会很慢,但方式不同。它会使您的查询更快,但会将您执行的所有插入操作放慢到TimeCard。如果您确定决定使用Trigger,那么我会确保我也将该索引编入索引,或者也可能很慢,而不是慢3分钟,但排序和返回数据的速度仍然很慢。

答案 2 :(得分:0)

我不会用这个视图。我认为触发器填充的表是要走的路。但是不要忘记调整更新和删除以及插入的总数。

答案 3 :(得分:0)

我不认为,你需要索引视图(我不说,索引视图是坏/好主意)。 我想,您需要“CompanyID”和“EntryDate”列上的索引。之后你应该使用条件“WHERE CompanyID = @CompanyID AND EntryDate> = @StartDate AND EntryDate< = @EndDate”。

如果表由“EntryDate”主要处理,则可以在“EntryDate”列上使用集群索引。

在此之后,我认为select语句会比现在快得多。

答案 4 :(得分:0)

您是否考虑过分区表格?您可以考虑列表和散列分区表的组合。

答案 5 :(得分:0)

好吧,索引视图的想法非常好,如果你可以在它上面创建一个聚簇索引 - 完美。它应该很快 - 比查询好3分钟!

另一方面:如果只更新那些信息块,例如每月一次或每周一次(甚至每晚一次),最好将它们放入单独的DailyTimeCard表中,该表由例如填写/更新。定期发布SSIS包。

我不建议使用触发器来不断更新这样的事实表 - 如果你真的非常需要在当天的每一秒都拥有最新的数据,那么请坚持下去索引视图。

但是,你的索引视图确实有点繁重 - 它总结,它分组等等。在您的基础TimeCard表更改并更新时,始终保持最新状态,这将导致系统负载 - 很难说多少 - 但它可能非常明显。

如果您找到一种方法来提取所需的信息 - 分组和求和一次,然后将聚合数据存储到一个单独的事实表中 - 您应该同时拥有:DailyTimeCard表上的快速和快速查询,以及系统的其余部分应该减少负担,使索引视图始终保持最新状态。

也许这不是你想要的解决方案 - 但只是稍微考虑一下。它可能 - 或者可能不会 - 为你效劳!