PostgreSQL合并前与合并后的性能差异

时间:2018-08-27 15:42:51

标签: sql postgresql sql-execution-plan

我有3张桌子:

create table cart (
  id       bigserial primary key,
  buyer_id bigint unique not null
);


create table contact_person (
  id           bigserial primary key,
  cart_id      bigint references cart (id) not null unique,
  phone_number jsonb,
  first_name   VARCHAR,
  middle_name  VARCHAR,
  last_name    VARCHAR
);

create table cart_items (
  id      bigserial primary key,
  item_id bigint                      not null,
  cart_id bigint references cart (id) not null,
  count   int                         not null,
  unique (item_id, cart_id)
);

购物车:contact_person与 1:1
相关 cart:cart_items 1:N

我想按购物车ID汇总所有cart_items字段。 有2个选项:

1)在加入之前进行汇总:

select c.id       as id,
               c.buyer_id as buyer_id,
               cp.id      as contact_id,
               cp.phone_number,
               cp.first_name,
               cp.middle_name,
               cp.last_name,
               ci.ids, ci.item_ids, ci.counts
        from cart c
               inner join contact_person cp on c.id = cp.cart_id
               left join (select cart_id, array_agg(id) as ids, array_agg(item_id) as item_ids, array_agg(count) as counts
                          from cart_items ci
                          group by cart_id) ci on ci.cart_id = c.id
        where c.buyer_id = :buyerId;

2)加入后汇总:

select c.id       as id,
               c.buyer_id as buyer_id,
               cp.id      as contact_id,
               cp.phone_number,
               cp.first_name,
               cp.middle_name,
               cp.last_name,
               array_agg(ci.id) as ids,
               array_agg(ci.item_id) as item_ids,
               array_agg(ci.count) as counts
        from cart c
               inner join contact_person cp on c.id = cp.cart_id
               left join cart_items ci on ci.cart_id = c.id
        where c.buyer_id = :buyerId
group by c.id, cp.id;

正如Explain所示,联接后具有聚合的查询要快得多。 查询计划实际上是不同的,但是我无法解释为什么在聚合之前要花费如此高的成本。

1)之前汇总:

Nested Loop  (cost=108.97..141.16 rows=1 width=248)
  ->  Merge Left Join  (cost=108.82..132.96 rows=1 width=112)
        Merge Cond: (c.id = ci.cart_id)
        ->  Sort  (cost=8.18..8.19 rows=1 width=16)
              Sort Key: c.id
              ->  Index Scan using cart_buyer_id_key on cart c  (cost=0.15..8.17 rows=1 width=16)
                    Index Cond: (buyer_id = 1)
        ->  GroupAggregate  (cost=100.64..122.26 rows=200 width=104)
              Group Key: ci.cart_id
              ->  Sort  (cost=100.64..104.26 rows=1450 width=28)
                    Sort Key: ci.cart_id
                    ->  Seq Scan on cart_items ci  (cost=0.00..24.50 rows=1450 width=28)
  ->  Index Scan using contact_person_cart_id_key on contact_person cp  (cost=0.15..8.17 rows=1 width=144)
        Index Cond: (cart_id = c.id)

2)在以下时间聚合:

GroupAggregate  (cost=41.62..41.66 rows=1 width=248)
  Group Key: c.id, cp.id
  ->  Sort  (cost=41.62..41.63 rows=1 width=172)
        Sort Key: c.id, cp.id
        ->  Nested Loop Left Join  (cost=15.33..41.61 rows=1 width=172)
              ->  Nested Loop  (cost=0.30..16.37 rows=1 width=152)
                    ->  Index Scan using cart_buyer_id_key on cart c  (cost=0.15..8.17 rows=1 width=16)
                          Index Cond: (buyer_id = 1)
                    ->  Index Scan using contact_person_cart_id_key on contact_person cp  (cost=0.15..8.17 rows=1 width=144)
                          Index Cond: (cart_id = c.id)
              ->  Bitmap Heap Scan on cart_items ci  (cost=15.03..25.17 rows=7 width=28)
                    Recheck Cond: (cart_id = c.id)
                    ->  Bitmap Index Scan on cart_items_item_id_cart_id_key  (cost=0.00..15.03 rows=7 width=0)
                          Index Cond: (cart_id = c.id)

我想在cart_id字段上添加一个对cart_items的索引,这有效地加速了查询,但是在第一种情况下,就像在第二种情况下那样。 您如何解释这种差异?

1 个答案:

答案 0 :(得分:1)

这样想:在您之前的示例中,您正在联接一个表和一个“运行中”视图,必须在联接之前将其生成。

在您的“之后”示例中,您要联接2个表,然后进行汇总。联接本身更快,不需要创建,排序等。在不删除任何行的情况下汇总所有数据后,它应该会更快。而且无论如何联接都非常简单。