我希望创建非规范化的数据访问,主要是为了报告目的(从而避免连接和获得性能)。我有两个解决方案,但我正在寻找(a)其他可能的解决方案,以及(b)我应该考虑哪些权衡。我使用的是SQL Server 2008 R2。
在一个解决方案中,我可以在查询上创建一个索引视图,该视图执行我关心的连接。我的理解是,这确实在幕后实现,但是很棘手,并且可能无法保证良好的表现(并且存在关于观点表现的激烈辩论)。
在另一个解决方案中,我可以构建机制来创建一个表,用我关心的数据填充它,并在事务中将它交换为现有表。
前者对我来说似乎有风险/神奇;后者看起来很笨拙,容易出错,并可能影响查询计划之类的事情。有人可以帮忙解释一下吗?
答案 0 :(得分:2)
在优化管道的早期阶段,视图被内联到查询计划中。他们既不受伤也不改善表现。
索引视图也会内联。您是否已编写查询以引用视图或是否已粘贴视图定义并不重要。在Enterprise Edition上,优化程序尝试在管道中稍后将索引的部分内容与索引视图进行匹配。这确实不可靠。对于简单的情况,它工作正常但我已经看到它随机失败。无法保证。
有一个解决方案:WITH (NOINDEX)
强制优化器不内联视图但使用其索引。这是100%可靠的。
如果您可以将查询模式放入索引视图并且能够使用该提示(您可以创建始终包含该提示的包装器视图),那么索引视图是一个很好的自动且一致的解决方案。
前者对我来说似乎有风险/神奇
有了这个提示,索引视图中几乎没有任何意外。如果它有帮助:我有索引视图的生产经验。他们工作正常。
后者似乎很笨拙,错误 容易发生,并可能影响查询计划之类的事情。有人可以帮忙吗 对此有所了解吗?
这是事实。每个bug都有可能导致数据损坏。最好不要忘记写入数据的任何地方,否则你的非规范化数据会变得不一致。
如果没有充分理由拒绝使用索引视图,我建议您使用索引视图。
答案 1 :(得分:2)
首先,索引视图有两个问题:
1)基表中的更新行必须能够传播到索引视图,而不必引用基表中的任何其他行。这就是你可以使用SUM()和COUNT_BIG()的原因,因为只要知道更改的行中的内容就可以更新这些聚合,但是你不能使用MIN()或MAX(),因为你必须回顾基表以确保。
对索引视图(没有TVF,没有子查询,没有工会等)的所有看似任意的限制都归结为上述原因。引擎必须能够在不经常查看整个基表的情况下维护索引。
2)在更新索引视图之前,仍会处理基表上的AFTER触发器。
这限制了单独使用索引视图可以完成的复杂性。
其次,测试非规范化实际上会有所帮助。如果您只是实例化简单连接并且您已经受到I / O限制,那将使瓶颈变得更糟。另一方面,如果您预先计算大型聚合或采用极宽连接的垂直切片,则更有可能是一种改进。
第三,要使用索引视图,请使用如下模式:
CREATE TABLE huge_data_table ( ... )
GO
CREATE VIEW huge_data_table_monthly_summary_index AS
SELECT
YEAR(...) AS [year_...]
,MONTH(...) AS [month_...]
,SUM(...) AS [sum_...]
,COUNT_BIG(*) AS [count_...]
FROM huge_data_table
GROUP BY YEAR(...),MONTH(...)
GO
CREATE UNIQUE CLUSTERED INDEX UC__xyz
ON huge_data_table_monthly_summary_index
([year_...],[month_...])
GO
CREATE VIEW monthly_summary AS
SELECT
[year_...]
,[month_...]
,[sum_...]
,[count_...]
FROM huge_data_table_monthly_summary_index WITH (NOEXPAND) --force use of the view's index
GO
然后,您可以查询monthly_summary
并获取聚合,而无需每次都重新计算它们。只要huge_data_table_monthly_summary_index
发生变化,引擎就会自动更新huge_data_table
。
它可能看起来很神奇,但它确实有效。尽可能使用它来代替触发器/作业/等来同步表。我发现我通常可以将复杂的报告作业分解为可以使用索引视图的更小,更简单的部分,并且在报告时动态加入中间结果会足够快以完成工作。
如果这不足以满足您的需求,您可能正处于日志传送领域或镜像到单独的报表服务器。手动同步是有意义的,没有多少中间地带。