1

当我将下一个查询放入函数时,它会慢 76 倍。计划中的唯一区别是:位图索引扫描 VS 索引扫描

计划1: http ://tatiyants.com/pev/#/plans/plan_1562919134481 在此处输入图像描述

计划2: http ://tatiyants.com/pev/#/plans/plan_1562918860704 在此处输入图像描述

计划1

EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)
        SELECT
            sum( t.group_suma ) OVER( PARTITION BY (t.o).id ) AS total_suma,
            *
        FROM (
            SELECT
             sum( ocd.item_cost     ) AS group_cost,
             sum( ocd.item_suma     ) AS group_suma,
             max( (ocd.ic).consumed ) AS consumed,
             (ocd.ic).consumed_period,
             ocd.o
            FROM order_cost_details( tstzrange( '2019-04-01', '2019-05-01' ) ) ocd
            GROUP BY ocd.o, (ocd.ic).consumed_period
        ) t
WHERE (t.o).id IN ( 6154 ) AND t.consumed_period @> '2019-04-01'::timestamptz
;

计划2

EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)
SELECT * FROM order_total_suma( tstzrange( '2019-04-01', '2019-05-01' ) ) ots 
WHERE (ots.o).id IN ( 6154 ) AND ots.consumed_period @> '2019-04-01'::timestamptz
;

功能:

CREATE FUNCTION "order_total_suma" (in _target_range tstzrange default app_period())
 RETURNS    table(
        total_suma  double precision,
        group_cost  double precision,
        group_suma  double precision,
        consumed    double precision,
        consumed_period tstzrange,
        o order_bt
    )

 LANGUAGE sql
 STABLE
 AS $$
    SELECT
        sum( t.group_suma ) OVER( PARTITION BY (t.o).id ) AS total_suma,
        *
    FROM (
        SELECT
         sum( ocd.item_cost     ) AS group_cost,
         sum( ocd.item_suma     ) AS group_suma,
         max( (ocd.ic).consumed ) AS consumed,
         (ocd.ic).consumed_period,
         ocd.o
        FROM order_cost_details( _target_range ) ocd
        GROUP BY ocd.o, (ocd.ic).consumed_period
    ) t
$$
;

为什么对于函数内部的查询,过滤是在最后一次子查询扫描时完成的?

在此处输入图像描述

有没有可能做一些事情让他们平等地工作?

UPD
服务器版本是PostgreSQL 12beta2
由于 30000 个字符的限制,我在此处此处发布计划

4

2 回答 2

2

感谢 IRC 的RhodiumToad

我怀疑有些东西阻止了计划者能够推断出 (to).id 可以安全地通过 GROUP BY ocd.o

可以通过使其成为自己的单独列来解决

因此我另外添加GROUP BY了 odc.id 列。所以我的最终查询是:

    SELECT * FROM (
            SELECT
                sum( t.group_suma ) OVER( PARTITION BY t.order_id ) AS total_suma,
--              sum( t.group_suma ) OVER( PARTITION BY (t.o).id ) AS total_suma,  -- For any WHERE this takes 2700ms
                *
            FROM (
                SELECT
                 sum( ocd.item_cost     ) AS group_cost,
                 sum( ocd.item_suma     ) AS group_suma,
                 max( (ocd.ic).consumed ) AS consumed,
                 (ocd.ic).consumed_period,
                 ocd.o,
                 (ocd.o).id as order_id
                FROM order_cost_details( tstzrange( '2019-04-01', '2019-05-01' ) ) ocd
                GROUP BY ocd.o, (ocd.o).id, (ocd.ic).consumed_period
            ) t
    ) t
    WHERE t.order_id = 6154 AND t.consumed_period @> '2019-04-01'::timestamptz       -- This takes 2ms
--  WHERE (t.o).id = 6154 AND t.consumed_period @> '2019-04-01'::timestamptz   -- This takes 2700ms

此更改还使通过函数调用更快。我只需要通过order_id字段排序:

SELECT * FROM order_total_suma( tstzrange( '2019-04-01', '2019-05-01' ) ) ots 
-- This WHERE takes 2.5ms
WHERE ots.order_id IN ( 6154 ) AND ots.consumed_period @> '2019-04-01'::timestamptz
-- This WHERE takes 2500ms
-- WHERE (ots.o).id IN ( 6154 ) AND ots.consumed_period @> '2019-04-01'::timestamptz
于 2019-07-12T09:58:52.390 回答
0

计划完全不同。

问题是对子查询public.order_btsplit_period子查询之间连接的结果计数的错误估计。这导致函数public.service_level_price被评估 2882 次而不是一次,这是花费时间的地方。

不知道该怎么做(我们没有视图定义,它可能很讨厌)。提高COST函数的 可能无济于事,因为优化器认为它只会调用一次。

实际上,最好的选择可能是

ALTER FUNCTION public.calc_item_suma ROWS 1;

这可能会让优化器选择不同的计划。

于 2019-07-12T09:18:55.883 回答