2016-03-21 53 views
16

此问题涉及使用其他函数或解决方法的first_value()的功能。从分组列中采样重复值的最佳性能

这也是关于“大表中的小增益”。使用例如。 max()在下面解释的上下文中,要求虚假的比较。即使速度很快,也会带来一些额外的成本。


这种典型的查询

SELECT x, y, count(*) as n 
FROM t 
GROUP BY x, y; 

需要重复所有列在GROUP BY返回多个列。一个语法糖要做到这一点,是使用位置引用:

SELECT x, y, count(*) as n 
FROM t 
GROUP BY x, 2 -- imagine that 2, 3, etc. are repeated with x 

有时不仅需要糖,但也有一些语义理解复杂的情境:

SELECT x, COALESCE(y,z), count(*) as n 
FROM t 
GROUP BY x, y, z -- y and z are not "real need" grouping clauses? 

我可以想像其他许多复杂的环境。让我们来看看常用解决方案:

SELECT x, max(y) as y, count(*) as n 
FROM t 
GROUP BY x -- best semantic! no need for other columns here 

其中max()功能可以是任何“样本()”(例如第一个或最后一个值)。无所事事的表现优于max(),例如,聚合函数first_value(),但它需要一个WINDOW,所以性能下降。有一些旧建议to implement first/last agg functions in C

是否有一些“获得任意一个值的快速”聚合函数,性能比max()GROUP BY X,2,...更好?
也许最近发布的一些新功能?

+1

请[编辑]你的问题,并添加基于该数据的一些样本数据和预期输出。如果你在列上有一个索引,max()'将会非常快。你可能希望查看'limit'或'distinct on()'还有,如果你的查询很慢,那么使用'explain(analyze,verbose)' –

+0

来提供查询,全表定义和执行计划。 '不懂'max()'函数的意思可以是任何“sample()”。你的意思是“聚合函数”?另外如果问题是如何使聚合函数更快,那么关于句法糖的所有介绍都与它有关? –

+0

有一种方法来模拟postgres上的松散索引扫描,这将是最快的https://wiki.postgresql.org/wiki/Loose_indexscan – Mihai

回答

5

不是一个官方来源,但一些想法的认为是比较通用的一个问题:

一般聚合neeed处理所有匹配的行。从您的问题文本中,您可能会尝试识别特定值(最大值,最小值,第一个,最后一个,第n个等)的聚合器。这些可以从数据结构中受益,这些数据结构为特定的聚合器维护适当的值。然后“选择”该值可以大大加快。
例如有些数据库会跟踪列的最大值和最小值。
您可以将此支持视为由系统自身维护且不受用户(直接)控制的高度专业化的内部索引。

现在,postgresql更侧重于支持,有助于改善一般查询,而不仅仅是特殊情况。因此,他们避免增加加速特殊情况的努力,这些情况显然不利于广泛的用例。

返回加快样本价值聚合器。

由于聚合器在一般情况下不得不处理所有行,并且没有提供允许尝试标识特定值的聚集器(现在是样本类型聚合器)的短路的一般策略,显而易见的是,任何重新查询这不会导致需要处理的减少的行集合将花费相似的时间来完成。

为了加速处理所有行之外的查询,您需要一个支持的数据结构。数据库通常以索引的形式提供。

您还可以从允许减少要读取的行数的特殊执行操作中受益。

使用pg,您可以提供自己的索引实现。所以,你可以添加最支持一种特殊的聚合你有兴趣的实现。(至少在地方,你需要经常运行这些查询的情况下)。

此外,执行操作,如指数仅扫描用递归查询进行延迟评估可能允许以与“直接”编码相比速度的方式编写特定查询。

如果您将您的问题更多地转化为一般方法,您可能更好地咨询研究人员有关这些主题的内容,因为这样做超出了所有目标。

如果您有特定(一组)查询需要改进,提供有关这些查询的明确问题可能允许社区帮助确定潜在的优化。试图在没有良好测量基础的情况下进行优化并不合适,因为在一种情况下会产生完美结果,可能会导致另一种情况下性能下降

+0

谢谢@rpy!你能用SQL代码示例来说明吗? (或引用我的示例的上下文) –

+0

我真的不相信展示示例将有助于未知场景。由于索引(唯一)扫描,在列上具有索引的E.f将加速“选择最大(列)...”查询。 (唯一的部分仅适用于表中有更多列的情况。)稍微改变查询以包含一个条件,例如, '从wherecolumn = SOMEVALUE'的表中选择max(column)可能会导致索引无用。然后在两列上有两个单独的索引或在'othercolumn,column'上有一个组合索引可能是需要的。 – rpy

+0

只要将连接引入查询,整个情况就会变得更加复杂。因此,不要期望SOMEAGGREGATE()具有良好性能时的一般规则,然后执行以下操作..._。看看你的查询,检查查询计划,可能会确定相关值的分布(那些不能从pg统计数据获得),然后开始优化。 – rpy

7

如果你真的不在乎其设定的成员摘下来的,如果您不需要额外的计算骨料(如计数),有一个与DISTINCT ON (x)一个快速和简单的选择,而不ORDER BY

SELECT DISTINCT ON (x) x, y, z FROM t; 

xyz来自相同的行,但是行是从各组行具有相同x任意选择。

无论如何,如果您需要计数,您在性能方面的选择受到限制,因为在任何情况下都必须阅读整个表格。不过,你可以在同一个SELECT与窗口函数结合起来:

SELECT DISTINCT ON (x) x, y, z, count(*) OVER (PARTITION BY x) AS x_count FROM t; 

考虑事件的序列中的SELECT查询:

根据要求,有可能是获得计数的更快方法:

在结合GROUP BY唯一现实的选择我看到获得一些性能是first_last_agg extension。但不要期望太多。

对于没有计数的其他用例(包括顶部的简单情况),根据您的确切用例,有更快的解决方案。特别要获得每组的“第一”或“最后”值。模拟松散的索引扫描。(像@Mihai commented):

+0

再次感谢您。我会测试[first_last_agg](http://pgxn.org/dist/first_last_agg/),看起来我需要(!)...然后会在这里(几天后)回复它,并进行讨论。 –

+1

...我[在Github上等待first_last评论](https://github.com/wulczer/first_last_agg/issues/2)...但是做一些家庭作业:'DISTINCT ON'不是一个直接的解决方案,因为,正如你所评论的[我测试过的](http://dba.stackexchange.com/q/133520/90651),不优化'GROUP BY',既不会导致从子句中删除列。理想的解决方案是[Craig在此引用](http://stackoverflow.com/a/8373384/287948),是在MySQL 5.7+中定义的'ANY_VALUE()',它为此任务提供了正确的语义(和SQL解析器决定是否使用第一个或最后一个样本)。 –

+0

@PeterKrauss:我添加了一个选项来将聚合与“DISTINCT ON”结合起来。 –