Лучший способ получить четкое количество от запроса, объединяющего две таблицы - PullRequest
1 голос
/ 04 апреля 2020

У меня есть 2 таблицы, таблица A и таблица B.

Таблица A (имеет тысячи строк)

  • id
  • uuid
  • имя
  • тип
  • create_by
  • org_id

Таблица B (имеет не более ста строк)

  • org_id
  • org_name

Я пытаюсь получить наилучший запрос на соединение для получения числа с предложением WHERE. Мне нужно количество различных created_by s из таблицы A с org_name в таблице B, которая содержит 'myorg'. В настоящее время у меня есть запрос ниже (выдача ожидаемых результатов), и мне интересно, можно ли его оптимизировать дальше?

select count(distinct a.created_by)
from a left join
     b
     on a.org_id = b.org_id 
where b.org_name like '%myorg%';

Ответы [ 3 ]

1 голос
/ 04 апреля 2020

Я бы использовал exists для этого:

select count(distinct a.created_by)
from a
where exists (select 1 from b where b.org_id = a.org_id and b.org_name like '%myorg%')

Индекс на b(org_id) поможет. Но с точки зрения производительности ключевыми моментами являются:

  • поиск с использованием like с подстановочными знаками с обеих сторон не является хорошим для производительности (это не может использовать индекс); было бы гораздо лучше найти точное совпадение или, по крайней мере, не использовать подстановочный знак в левой части строки.

  • count(distinct ...) дороже обычного count(); если вы не действительно нуждаетесь distinct, то не используйте его.

1 голос
/ 04 апреля 2020

Ваш запрос уже выглядит хорошо. Вместо него используйте [INNER] JOIN или LEFT [OUTER] JOIN, как предложил Гордон. Но это не сильно изменится.

Вы упомянули, что в таблице B есть только ...

максимум из ста строк

в то время как таблица A имеет ...

тысячи строк

Если , то в created_by имеется много строк (что и следовало ожидать), то есть вероятность для эмулируемого сканирования с пропуском индекса .
(необходимость его эмуляции может go удалить в одном из следующих Postgres версий .)

Основным ингредиентом является многоколонный индекс :

CREATE INDEX ON a (org_id, created_by);

Может замените простым индексом на (org_id) и он также работает для вашего простого запроса. См .:

В вашем случае есть два осложнения:

  1. DISTINCT
  2. 0-n org_id в результате org_name like '%myorg%'

Таким образом, реализовать оптимизацию сложнее. Но все еще возможно с некоторой фантазией SQL:

SELECT count(DISTINCT created_by)  -- does not count NULL (as desired)
FROM   b
CROSS  JOIN LATERAL (
   WITH RECURSIVE t AS (
      (  -- parentheses required
      SELECT created_by
      FROM   a
      WHERE  org_id = b.org_id
      ORDER  BY created_by
      LIMIT 1
      )
      UNION ALL
      SELECT (SELECT created_by
              FROM   a
              WHERE  org_id = b.org_id
              AND    created_by > t.created_by
              ORDER  BY created_by
              LIMIT  1)
      FROM   t
      WHERE  t.created_by IS NOT NULL  -- stop recursion
      )
   TABLE t
   ) a
WHERE  b.org_name LIKE '%myorg%';

db <> fiddle здесь (Postgres 12, но работает в Postgres 9.6 как хорошо.)

Это рекурсивный CTE в подзапросе LATERAL, использующий коррелированный подзапрос.

Он использует многостолбцовый индекс сверху для получения только один ряд для каждого (org_id, created_by). При сканировании только по индексу, если таблица достаточно вакуумирована.

Основная цель изощренного SQL - полностью избежать последовательного сканирования (или даже сканирования индекса растрового изображения) при большая таблица и читает только очень мало быстрых индексных кортежей.

Из-за дополнительных издержек это может быть немного медленнее для неблагоприятного распределения данных ( много org_id и / или только несколько строк на created_by) Но это намного быстрее для благоприятных условий и отлично масштабируется, даже для миллионов строк. Вам нужно проверить, чтобы найти наилучшее место.

Связанный:

1 голос
/ 04 апреля 2020

Вам не нужно left join:

select count(distinct a.created_by)
from a join
     b
     on a.org_id = b.org_id
where b.org_name like '%myorg%' 

Для этого запроса вам нужен индекс на b.org_id, который, я полагаю, у вас есть.

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