Агрегирование PostgreSQL перед объединением после разницы в производительности объединения - PullRequest
0 голосов
/ 27 августа 2018

У меня есть 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

И я хочу собрать все поля 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 голосов
/ 27 августа 2018

Подумайте об этом так: в приведенном выше примере вы объединяете таблицу и представление «на лету», и должны быть сгенерированы ДО того, как их можно будет объединить.

В вашем примере "после" вы объединяете 2 таблицы и затем агрегируете. Само объединение происходит быстрее, и его не нужно создавать, сортировать и т. Д. Агрегирование данных ПОСЛЕ того, как вы собрали все, должно быть быстрее, если вы не удаляете какие-либо строки ... и объединение в любом случае намного проще.

...