6

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

我现在需要生成一份报告,以找出任何给定公司每个项目每月员工的总工作时间。我不想在报表运行时执行所需的聚合,而是想构建一个类似表格的数据结构,其中已经包含按月聚合的所有公司/项目/用户数据,因此当报表运行时,我可以直接查询该数据结构无需执行任何运行时聚合,因为大约 3500 万条记录可能需要几分钟。

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

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)或针对它运行查询时是否重新计算/重新聚合整个索引视图?

谢谢!

4

6 回答 6

2

如果您没有使用EnterpriseDeveloper版本,那么您需要使用with (noexpand)提示:

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

当底层数据发生变化时,视图只会更新与变化数据相关的行,而不是整个表。这可能会对具有高度插入的 OLTP 数据库产生不利影响,但如果使用量适中,则不会造成性能问题。

来自微软的提示:

作为一般建议,对视图或视图下的基表的任何修改或更新都应尽可能分批执行,而不是单例操作。这可能会减少视图维护中的一些开销。

于 2010-03-10T19:46:02.123 回答
1

我认为您使用索引视图走在正确的道路上。但是,您是否在要查询的表上TimeCard 为聚合列放置了索引。您需要创建一个索引 JobID, ProjectID, entryDate, CompanyID(1 个索引)。如果您为每列使用 1 个索引,它不会解决您的问题,因为查询必须一起使用所有 4 个索引。

我确实认为使用触发器会很慢,但方式不同。它会使您的查询更快,但会减慢您对TimeCard. 如果您确实决定使用触发器,那么我会确保我也索引该表,或者也可能很慢,不是慢 3 分钟,但排序和返回数据仍然很慢。

于 2010-03-10T19:51:01.797 回答
0

您是否考虑过对表进行分区。您可以想到列表和哈希分区表的组合。

于 2010-03-10T20:02:11.567 回答
0

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

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

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

于 2010-03-10T19:46:38.797 回答
0

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

于 2010-03-10T19:45:58.370 回答
0

好吧,索引视图的想法绝对是好的,如果你可以在它上面创建一个聚集索引 - 完美。它应该很快 - 比 3 分钟的查询要好得多!

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

我也不建议使用触发器来不断更新这样的事实表——如果您真的需要在一天中的每一秒都拥有最新的数据,那么请使用索引视图。

但是,您的索引视图做了很多繁重的工作 - 它求和,它分组等等。在您的基础表更改和更新时始终保持最新状态TimeCard,这将导致您的系统出现一些负载 - 很难说有多少 - 但它可能非常明显。

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

也许这不是您正在寻找的解决方案 - 但请考虑一下。它可能(也可能不会)适合您!

于 2010-03-10T20:43:32.590 回答