在大内连接上有效地计算和SUMing

时间:2012-11-07 19:59:56

标签: sql-server tsql inner-join

请考虑以下情况(为了问题而简化):

我在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列的总和 - 每次都不同。

我怀疑直接实现(使用内部联接等)可能效率极低,更好的解决方案可能包括其他表,某种计数器表或我应该在我的服务代码中实现这一点(?!)并以某种方式照顾持久性。

实施上述的“正确”(效率)方式是什么?处理额外级别的父母/子女的解决方案将是最好的。

2 个答案:

答案 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