Оптимизация объединенных внутренних соединений? - PullRequest
0 голосов
/ 17 апреля 2019

Меня интересует оптимизация времени выполнения запроса:

create temp table table_base as
(
  SELECT table_a.* FROM source_A a
  INNER JOIN
  source_B b
  USING(common_field1)
  WHERE a.field1 = x AND a.field2 = y
) 
UNION
(
  SELECT a.* FROM source_C a
  INNER JOIN
  source_B b
  USING(common_field1)
  WHERE a.field1 = x AND a.field2 = y
) UNION...

Первая таблица в каждом подзапросе (в этом примере source_A и source_C) имеет идентичную структуру, но наибольшее количество данных. Необходимо внутреннее соединение с source_B. Мне интересно добавить поле и удалить значения, недоступные в общем поле source_B.

Сам запрос в настоящий момент выполняется, но мне интересно, есть ли более быстрый способ сделать это. Я уже разместил предложения WHERE в подзапросе, а не в конце объединения, но меня удивляет, быстрее ли сначала объединить / объединить все, а затем выполнить одно внутреннее соединение?

Любая помощь очень ценится (даже если это означает, что это как-то самая эффективная структура запроса).

Ответы [ 4 ]

1 голос
/ 17 апреля 2019

Для повышения производительности одним из ключевых аспектов является удаление невыбранных строк как можно скорее. Наличие двух отдельных объединений - хороший выбор, поскольку вы заставляете объединения сразу исключать несовпадающие строки.

Но сначала вам нужно убедиться, что движок использует предикат самого быстрого доступа для фильтрации данных из массивных таблиц source_A и source_C. Для ваших предикатов фильтрации вы используете простые равенства ... это идеально. Я бы добавил следующие индексы в ваши таблицы (если у вас их еще нет):

create index ix1 on source_A (field1, field2);
create index ix2 on source_C (field1, field2);

Теперь, после выбора строк из source_A и source_C вам потребуется доступ к source_B. Чтобы эта задача была быстрой, вам необходимо убедиться, что следующий индекс также присутствует:

create index ix3 on source_B (common_field);

Вы также упоминаете, что можно использовать некоторые столбцы из source_B. Чтобы сделать это незначительно быстрее, вы можете попробовать на нем индекс покрытия. Например, если вы используете столбец name из source_B, я бы преобразовал этот индекс в индекс покрытия, как в:

create index ix3 on source_B (common_field, name); -- added column "name"

Наконец, после внесения этих изменений вы должны получить план выполнения, чтобы увидеть, что делает оптимизатор SQL. Это никогда не повредит, и покажет вам подробно, если он выбирает лучший путь или нет. Используйте EXPLAIN, чтобы получить его.

1 голос
/ 17 апреля 2019

Если вы не хотите удалять повторяющиеся строки, используйте UNION ALL вместо UNION, и это будет значительно быстрее.

И обычно быстрее всего применять WHEREпункты как можно раньше в процессе - то есть, как у вас сейчас.Также позволяет использовать индексы, где это применимо.Было бы невозможно, если бы вы применили WHERE к результату UNION / UNION ALL.

И вы действительно имеете в виду INNER JOIN source_B b?Тем самым возможно умножение строк в a?Намерение может быть (чтобы избежать дублирования):

SELECT *
FROM   source_A a
WHERE  a.field1 = x
AND    a.field2 = y
AND    EXISTS (SELECT 1 FROM source_B b WHERE b.common_field1 = a.common_field1)

.. аналогично для source_C ...

Лучший запрос действительно зависит от характера отношений между source_Aи source_B, их количество и доступные индексы.
Вы можете присоединиться, чтобы отфильтровать предварительное условие существования в source_B, или умножить строки, или получить дополнительные столбцы (вы упомянули это).Но последнее не проясняет все это.Есть место для интерпретации ...

0 голосов
/ 22 апреля 2019
  1. Это зависит от вашей сортировки и распределения. Для этого запроса в идеале source_A ключ сортировки таблицы - (x,y), а ключ распределения всех таблиц - common_field1. Это может существенно повлиять на скорость вашего запроса независимо от того, как он написан.
  2. Как указано выше, UNION ALL обеспечивает очень хороший импульс, если у вас нет пересечений в наборах UNION, которые необходимо дедуплицировать.
  3. Я бы также попробовал запрос без объединения. Проверьте ниже:

-

SELECT source_A.* 
FROM source_A a
LEFT JOIN source_B b
USING(common_field1)
LEFT JOIN source_C c
USING(common_field1)
WHERE a.field1 = x 
AND a.field2 = y
AND (
    b.id is not null
    or c.id is not null
)
0 голосов
/ 17 апреля 2019

Примерно так:

    CREATE TEMP TABLE table_base as
(
  SELECT a.*
  FROM (
    (
      SELECT * FROM source_A WHERE a.field1 = x AND a.field2 = y
    )
    UNION
    (
      SELECT * FROM source_C WHERE a.field1 = x AND a.field2 = y
    )
  ) a
  JOIN source_B b 
  USING(common_field1)
)

Таким образом, вы делаете WHERE (и сокращаете количество записей) перед JOIN.Это вместе с индексами, добавленными к любым столбцам, используемым для JOIN, которые, скорее всего, не будут вашим лучшим вариантом

...