SQL聚合函数如何工作

时间:2013-02-26 05:44:35

标签: java sql sql-server oracle aggregate-functions

任何人都可以告诉我如何在SQL数据库中实现聚合函数,例如:Oracle或SQL Server。

我的意思是,当select子句中存在聚合函数时,这些数据库是否使用了一些内部数据结构或算法。

我之所以这样问是因为我在java ArrayList中有100,000条记录,当我尝试所有值的总和时,它需要大约1分钟但是当相同的100,000条记录存储在DB中时我会使用sum( column_nm)它几乎在1/4的时间内执行。

我想以类似的方式改进我的java代码性能,我想知道SQL聚合函数的内部结构。

感谢。

5 个答案:

答案 0 :(得分:2)

虽然这与内部定义的聚合的工作方式不完全匹配,但在SQL Server中,您可以创建user-defined aggregates。看到这样的聚合必须定义的方法可能是有益的:

  • Init
  

查询处理器使用此方法初始化聚合的计算。对于查询处理器正在聚合的每个组,将调用此方法一次。查询处理器可以选择重用聚合类的相同实例来计算多个组的聚合。 Init方法应该根据此实例的先前使用情况执行任何清理,并使其能够重新启动新的聚合计算。

  • Accumulate
  

...查询处理器使用此方法累积聚合值。对于正在聚合的组中的每个值,将调用一次。查询处理器总是在调用给定的aggregate-class实例上的Init方法之后才调用它。此方法的实现应更新实例的状态,以反映传入的参数值的累积。

  • Merge
  

此方法可用于将此聚合类的另一个实例与当前实例合并。查询处理器使用此方法合并聚合的多个部分计算。

  • Terminate
  

此方法完成聚合计算并返回聚合的结果。 ...

MergeTerminate的描述中,我们可以推断服务器可能并行执行单个组内的多个部分聚合。一旦发生了这些并行累加中的每一个,所有结果将Merge d在一起,然后在类的一个实例上对Terminate的最终调用产生最终的聚合结果。

因此,实现加速(如果可能)的一种显而易见的方法是并行化累积阶段。

答案 1 :(得分:2)

有一个非常简单的解释,为什么java代码要慢得多:

您正在使用ArrayList,因此我假设您将Integer-Objects放在那里。它们在某些堆栈中的C中具有显着的开销。 第二,当你总结它们并为每个部分总和创建另一个Integer时,你的GarbageCollector会吃掉所有的性能。

如其他答案所述,

  1. DB将使用直接数学处理器访问来在寄存器中添加整数 - 不能更快。
  2. 好的数据库不会仅迭代,而是映射+减少总和,最小或最大等聚合。因此,他们获得了多处理器的奖励,几乎忽略了I / O延迟。
  3. 您可以在代码中解决它:使用int []

     int[] parts;
     sum=0;
     for (int i:parts) {
       sum+=i;
     }
    

    如果根据处理器数量拆分(映射)数组并将其与Future进行并行化,则可能需要测试,这取决于数据的大小。

答案 2 :(得分:1)

性能差异仅仅是因为计算SUM,您不需要同时将所有数据存储在内存中。

当您向数据库发出直接询问SUM的查询时,它可以从磁盘读取每条记录,在内存中的单个变量中累计运行总计,然后读取下一条记录 - 它永远不需要保留所有记录在记忆中同时。更重要的是,它不需要通过网络将这些记录发送到任何其他服务器进行处理 - 它只需要在最后将结果SUM作为单个数字发送。

另外,因为总体上的SUM等于整体的任何不同子集的SUM,所以SUM可以被并行化 - 例如,如果数据是分区的,那么数据库可以发出多个查询以在不同的会话中运行,每个查询将对其部分数据进行SUM,然后控制会话可以简单地对每个分区的结果进行SUM。

当您使用数组计算Java程序中的总和时,它必须首先向数据库发出查询,询问它所需的所有数据;所有数据都需要从数据库传输到应用服务器,并且需要分配内存来存储所有数据。只有在那之后你的程序才会在内存中迭代数组并计算Sum;然后,它可能需要从内存中释放数组。

如果数据量较低,性能差异可能不大。但是,如果音量很大,那么差异可能会非常显着。

答案 3 :(得分:0)

聚合通常只是迭代结果集,然后执行聚合,无论是总和,平均值还是计数等。

如果你在谈论操作的复杂性,它几乎总是O(n),其中n是结果集中用于简单聚合的记录数。

我不明白为什么在java中需要花费更长的时间,因为你的数组会被实例化到主内存中,这比从磁盘中读取更快,就像RDBMS那样。老实说,来自RDBMS的聚合应该比arraylist聚合稍慢。

为了扩展这一点,如果你想要特定条目的一行(带有PK或索引),对于一个arraylist来说它是O(1)而对于一个具有适当索引的RDBMS来说是O(1)(对于一个标准的链表,获取该行将是o(n),但与聚合的arraylist相同。迭代整个数据集(无论是数组还是表),执行聚合几乎总是O(n)。

答案 4 :(得分:0)

有趣的问题。

精心编写的rdbms是数千名博士数学家和数据库专家的工作时间的高潮。你试图模仿MSSQL或postgressql的性能是令人钦佩的,但是在风车上倾斜(如果你不熟悉堂吉诃德,那就太难看了。)

与rdbms的一个常见误解是关系意味着相关的表。相关实际上是指数学关系。基本上 - rdbms专注于集合论。即使有很好的rdbms,开发人员也可以通过逐行计算事物来破坏性能,而不是使用固有的本机集。这实际上是您正在经历的性能差异的恰当比较。

如果您仅限于在java而不是db中执行此计算,则应该考虑优化数据结构(最小数据类型)和循环效率。你仍然无法与sql server或postgres竞争。如果你真的需要改进的性能,可能值得将这些项存储在数据库中并从java中调用它们。