从分组列中重复采样的最佳性能

Pet*_*uss 16 sql postgresql aggregate-functions postgresql-performance

这个问题是关于first_value()使用其他功能或解决方法的功能.

它也是关于大表中"性能上的微不足道".使用例如.max()在下面解释的上下文中,要求虚假比较.即使速度很快,也会产生一些额外的成本.


这种典型的查询

SELECT x, y, count(*) as n 
FROM t 
GROUP BY x, y;
Run Code Online (Sandbox Code Playgroud)

需要重复所有列GROUP BY以返回多个列.执行此操作的语法糖是使用位置引用:

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

有时不仅需要糖,还需要一些语义来理解复杂的上下文:

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

我可以想象许多其他复杂的背景.让我们看看通常的解决方案:

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

其中max()function可以是任何"sample()"(例如,first或last值).无所作为的东西的表现比例max()如聚合函数更好first_value(),但它需要一个WINDOW,因此失去了性能.在C中实现第一个/最后一个agg函数有一些旧的建议.

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

Erw*_*ter 8

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

SELECT DISTINCT ON (x) x, y, z FROM t;
Run Code Online (Sandbox Code Playgroud)

x,y并且z来自同一行,但该行是来自每组行的任意选择x.

如果您还需要计数,那么您在性能方面的选择是有限的,因为在任何一种情况下都必须读取整个表格.您仍然可以将它与窗口函数结合使用SELECT:

SELECT DISTINCT ON (x) x, y, z, count(*) OVER (PARTITION BY x) AS x_count FROM t;
Run Code Online (Sandbox Code Playgroud)

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

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

结合GROUP BY我看到获得一些性能的唯一现实选项是first_last_agg扩展.但是不要期待太多.

对于其他没有计数的用例(包括顶部的简单案例),有更快的解决方案,具体取决于您的确切用例.特别是获得每组的"第一"或"最后"值.模拟松散的索引扫描.(像@Mihai评论):


rpy*_*rpy 5

不是一个官方来源,但有些想法被认为是一个相当普遍的问题:

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

现在,postgresql更多地关注有助于改善查询的支持,而不仅仅是特殊情况.因此,他们避免加大力度来加速特殊情况,这些特殊情况并没有明显有利于广泛的用例.

回到加速样本价值聚合器.

由于聚合器必须在一般情况下处理所有行,并且没有一个通用策略允许短路对尝试识别特定值的聚合器的需求(现在的样本类聚合器),显然任何重构的查询都不会导致对于需要处理的减少的行集,将花费相似的时间来完成.

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

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

使用pg,您可以提供自己的索引实现.因此,您可以添加一个最能支持您感兴趣的特殊聚合器的实现.(至少在您需要经常运行此类查询的情况下.)

此外,诸如仅索引扫描具有递归查询的延迟评估的执行操作可允许以与"直接"编码相比速度快的方式编写特定查询.

如果您将问题更多地放在一般方法中,那么您可以更好地向研究人员咨询有关此类主题的内容,这些内容超出了SO旨在提供的任何内容.

如果您有需要改进的特定(一组)查询,那么提供有关这些查询的明确问题可能允许社区帮助识别潜在的优化.试图在没有良好测量基础的情况下进行优化无处可去,因为在一个案例中产生完美结果可能会在另一个案例中扼杀性能.