假设我有一个非常长的表(约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)或对其运行查询时是否重新计算/重新聚合整个索引视图?
谢谢!
答案 0 :(得分:2)
如果您未使用 Enterprise 或 Developer 版本,则需要使用with (noexpand)
提示:
select *
from vw_myView with (noexpand)
where companyID = 1
当基础数据发生更改时,视图将仅更新与已更改数据相关的行,而不是整个表。这可能会对具有高度插入的OLTP数据库产生负面影响,但如果使用率适中,则不应造成性能问题。
作为一般性建议,任何 修改或更新视图 或视图下的基表 如果是,应该分批进行 可能,而不是单身 操作。这可能会减少一些 视图维护中的开销。
答案 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
表上的快速和快速查询,以及系统的其余部分应该减少负担,使索引视图始终保持最新状态。
也许这不是你想要的解决方案 - 但只是稍微考虑一下。它可能 - 或者可能不会 - 为你效劳!