我遇到了一些性能问题,其中计算列平均值的SQL查询随着记录数量的增长而逐渐变慢。是否有可以添加到列中的索引类型,以便更快地进行平均计算?
有问题的数据库是PostgreSQL,我知道特定的索引类型可能不可用,但我也对理论答案很感兴趣,如果没有某种缓存解决方案,这甚至是天气。
更具体地说,有问题的数据本质上是一个具有这种定义的日志:
table log {
int duration
date time
string event
}
我在做像
这样的查询SELECT average(duration) FROM log WHERE event = 'finished'; # gets average time to completion
SELECT average(duration) FROM log WHERE event = 'finished' and date > $yesterday; # average today
第二个总是相当快,因为它具有更严格的WHERE子句,但总平均持续时间1是导致问题的查询类型。我知道我可以使用OLAP或其他东西来缓存这些值,我的问题是天气,我可以通过数据库方面的优化(如索引)完全实现这一点。
答案 0 :(得分:6)
计算平均值的性能总是会越慢,记录越多,总是必须使用结果中每条记录的值。
如果索引包含的数据少于表本身,则索引仍然可以提供帮助。为您想要平均值的字段创建索引通常没有帮助,因为您不想进行查找,您只想尽可能高效地获取所有数据。通常,您会将该字段作为输出字段添加到查询已使用的索引中。
答案 1 :(得分:2)
取决于你在做什么?如果您没有过滤数据,那么除了按顺序拥有聚簇索引之外,数据库还有什么方法来计算列的平均值?
有些系统执行在线分析处理(OLAP),可以执行诸如保持运行总和以及平均下来要检查的信息等操作。这一切都取决于你正在做什么以及你对“慢”的定义。
如果你有一个基于网络的程序,也许你可以每分钟生成一次平均值然后缓存它,一遍又一遍地向用户提供缓存的值。
答案 2 :(得分:2)
加速聚合通常是通过保留其他表来完成的。
如果您希望使AVG(或其他聚合函数)的性能几乎保持不变,无论记录数量是多少都可以引入新表,那么假设表detail(id, dimA, dimB, dimC, value)
相当大
dimAavg(dimA, avgValue)
n(dimA) x n(dimB) x n(dimC) x ...
,这可能会或可能不会很快增长。例如,让我们假设系统主要进行插入,偶尔更新和删除。
进一步假设您只想通过dimA进行分析,并且id
正在增加。然后有
dimA_agg(dimA, Total, Count, LastID)
可以在不对系统产生重大影响的情况下提供帮助。
这是因为你可能有触发器不会在每个插入物上触发,但是可以说在100个插入物上。
通过这种方式,您仍然可以从 这个详细信息表中获取准确的聚合
SELECT a.dimA, (SUM(d.value)+MAX(a.Total))/(COUNT(d.id)+MAX(a.Count)) as avgDimA
FROM details d INNER JOIN
dimA_agg a ON a.dimA = d.dimA AND d.id > a.LastID
GROUP BY a.dimA
上面带有正确索引的查询将从dimA_agg
获得一行,而detail
只有少于100行 - 这将在接近恒定的时间内执行(~log fanout n)并且不需要为每个插入更新dimA_agg
(减少更新惩罚)。
100的值仅作为示例给出,您应该自己找到最佳值(或者甚至保持变量,但在这种情况下触发器仅仅是不够的。)
维护删除和更新必须触发每个操作,但您仍然可以检查要删除或更新的记录的ID是否已经在统计信息中,以避免不必要的更新(将节省一些I / O)。 / p>
注意:对具有谨慎属性的域进行分析;在处理时间序列时,情况变得更加复杂 - 您必须确定要保留摘要的域的粒度。
修改强>
答案 3 :(得分:0)
只是一个猜测,但索引不会有多大帮助,因为平均值必须读取所有记录(按任何顺序),索引对行的查找子集很有用,如果你必须迭代所有行而没有特殊的排序索引没有帮助...
答案 4 :(得分:0)
这可能不是您正在寻找的,但如果您的表有某种方式来订购数据(例如按日期),那么您可以只进行增量计算并存储结果。
例如,如果您的数据有一个日期列,您可以计算记录1 - Date1的平均值,然后存储该批次的平均值以及Date1和您平均的#records。下次计算时,将查询限制为结果Date1..Date2,并添加记录数,并更新查询的最后日期。您拥有计算新平均值所需的所有信息。
执行此操作时,显示日期索引或您用于订购的任何列显然会有所帮助。