请考虑以下情况(为了问题而简化):
我在SQL Server 2012数据库中有以下表格:
Parent_Table
Id | Parent table fields
----+--------------------
1 | ...
2 | ...
3 | ...
...
Child_Table
Id | ParentId | Child table fields
----+----------+-------------------
1 | 2 | ...
2 | 1 | ...
3 | 1 | ...
4 | 3 | ...
5 | 2 | ...
...
Big_Table
Id | ChildId | Value | Status | Other fields
--------+----------+--------|---------------------
1 | 12 | 672 | Closed |
2 | 23 | 133 | Closed |
3 | 7 | 2611 | Open |
4 | 14 | 84 | Closed |
...
1295769 | 23 | 458 | Closed |
1295770 | 18 | 1046 | Open |
1295771 | 7 | 8 | Open |
Child和Parent表相对较小(每个父项大约有100个Parent条目和5个Child条目),并且每天只插入或删除几个条目。
另一方面,“大表”正在快速增长(为了讨论起见,每秒100个条目),并且一段时间后行的状态变为关闭(想想客户端会话,实际上是这里的情况)。我需要定期(每隔几秒)提供Big_Table行的数量以及指定Parent.Id 的Big_Table.Value列的总和 - 每次都不同。
我怀疑直接实现(使用内部联接等)可能效率极低,更好的解决方案可能包括其他表,某种计数器表或我应该在我的服务代码中实现这一点(?!)并以某种方式照顾持久性。
实施上述的“正确”(效率)方式是什么?处理额外级别的父母/子女的解决方案将是最好的。
答案 0 :(得分:3)
如果我们假设ChildId始终保留相同的ParentId,那么要考虑的一个选项是将ParentId添加到Big_Table。如果您确定某些行只会从您控制的应用程序代码添加到这些表中(而不是例如某个人自己运行INSERT语句的数据库连接),则可以在内存中保留父表和子表的缓存。插入Big_Table时,提供ChildId到ParentId的快速映射。那么你的sum / count查询将完全在Big_Table上完成。这种非规范化当然会增加Big_Table的大小,但如果这个和应用程序的变化是可以容忍的,那么这是一个可行的选择。
此外,如果ParentId是访问Big_Table中行(或性能问题最严重的行)的主要方式,那么您可以考虑在ParentId上对Big_Table进行分区。
完全不同的方法是将所需的统计信息直接存储在Parent_Table中,并使用Big_Table上的触发器来保持更新,或者,如果您控制应用程序代码中的所有数据库交互,则使用应用程序逻辑将统计信息更新为Big_Table插入的一部分。您可以在事务中执行此操作以确保一致性,或者如果您容忍某些错误,则可以将其作为异步统计信息更新来完成,以便Big_Table上的插入可以快速发生,而统计信息在后台更新。
至于处理父母和子女的额外水平,您可能需要在灵活性和效率之间做出选择。例如,您可能需要限制您愿意支持的树的深度,以便对深度逻辑进行硬编码以提高性能。
答案 1 :(得分:1)
我会对下面的2进行一次测试,如果合理的话你可以保持简单。我将留给你,以确定with(nolock)提示是否适合你的情况(数据库设置,数据的性质。)因为你知道父id你不需要Parent_Table。
显然应该有一个关于Big_Table.ChildId和Child_Table.ParentId的索引
select sum(Value)
from Big_Table with(nolock)
where ChildId in
(select Id
from Child_Table with(nolock)
where ParentId = @ParentId)
select sum(Value)
from Big_Table with(nolock)
inner join Child_Table with(nolock)
on Big_Table.ChildId = Child_Table.Id
where Child_Table.ParentId = @ParentId