Оптимизировать JOIN -> запрос GROUP BY в PostgreSQL: все индексы уже есть - PullRequest
0 голосов
/ 20 марта 2020

Есть по крайней мере пара похожих (но не совсем одинаковых ) вопросов по SO. В этих вопросах проблема производительности запросов заключается в отсутствии индексов или избыточных предикатов.

Но мой случай прост и понятен: 3 таблицы, каждая ссылается на другую. Есть индексы BTree для каждой строки таблицы ссылок . Вот таблицы:

CREATE TABLE region(
   id serial PRIMARY KEY,
   title VARCHAR (50) NOT NULL
);

CREATE TABLE unit(
   id serial PRIMARY KEY,
   region_id INT NOT NULL REFERENCES region(id)
);

CREATE TABLE unit_usage(
   id serial PRIMARY KEY,
   title VARCHAR (50) NOT NULL,
   unit_id INT NOT NULL REFERENCES unit(id)
);

CREATE INDEX ON unit ((region_id));
CREATE INDEX ON unit_usage ((unit_id));
CREATE INDEX ON unit_usage ((title));

В таблице unit_usage содержится более 300 000 000 строк, более 50 000 000 строк в таблице юнитов и более 65 000 строк в таблице регионов. Я хочу запросить количество регионов для каждого unit_usage . Примерно так:

WITH x AS
(
 select u.region_id as region_id, t.title as title
 from unit_usage t join unit u
 on t.unit_id = u.id
)
SELECT title, count(region_id) as found_in_regions
FROM x GROUP BY title;

Здесь ' DBFiddle .

Этот запрос выполняется около 5 минут. Это слишком много - мой лимит составляет около 10 секунд. Что я пробовал:

переформировать запрос, например:

select u.region_id, t.title, count(t.id) 
from unit_usage t join unit u
on t.unit_id = u.id group by u.region_id, t.title;

то же время выполнения.

  • setting enable_hashjoin = off; Я избавился от Ha sh Join и одного из Seq Scan, но это не влияет на время выполнения

Ответы [ 2 ]

0 голосов
/ 20 марта 2020

Этот запрос имеет эквивалентные результаты с вашим вторым запросом. Это может быть быстрее, потому что меньше строк для объединения:

with uu as (
  select u.unit_id, u.title, count(*) cnt
    from unit_usage u
   group by u.unit_id, u.title
)
select u.region_id, uu.title, sum(cnt)
  from uu
    inner join unit u
      on uu.unit_id = u.id
 group by u.region_id, uu.title

Этот индекс может быть полезен для этого запроса (лучше тестировать с индексом и без него):

create index unit_usage_ix on unit_usage(unit_id, title);
0 голосов
/ 20 марта 2020

Я бы сначала попытался получить правильную логику c. Если вы хотите посчитать количество отдельных регионов, то я бы ожидал:

Я хочу запросить количество регионов для каждого unit_usage.

select u.id, count(distinct u.region_id) 
from unit_usage uu join
     unit u
     on t.unit_id = u.id
group by u.id;

Это не ускорит запрос. Но по крайней мере это должно вернуть правильные результаты. Если это так, тогда вы можете начать думать о том, как сделать это правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...