Как улучшить производительность запросов с помощью множества JOINs

У меня есть запрос (с целью создания представления), который использует несколько объединений для получения каждого столбца. Производительность быстро ухудшается (экспоненциально?) Для каждого набора добавленных объединений.

Каким будет хороший способ ускорить этот запрос? Пожалуйста, просмотрите комментарии в запросе.

Если это помогает, это использует схему WordPress DB.

Вот скриншот EXPLAIN enter image description here

ТАБЛИЦА ПРОДУКТОВ

+--+----+
|id|name|
+--+----+
|1 |test|
+--+----+

ТАБЛИЦА METADATA

+----------+--------+-----+
|product_id|meta_key|value|
+----------+--------+-----+
|1         |price   |9.99 |
+----------+--------+-----+
|1         |sku     |ABC  |
+----------+--------+-----+

ТАБЛИЦА TERM_RELATIONSHIPS

+---------+----------------+
|object_id|term_taxonomy_id|
+---------+----------------+
|1        |1               |
+---------+----------------+
|1        |2               |
+---------+----------------+

TERM_TAXONOMY TABLE

+----------------+-------+--------+
|term_taxonomy_id|term_id|taxonomy|
+----------------+-------+--------+
|1               |1      |size    |
+----------------+-------+--------+
|2               |2      |stock   |
+----------------+-------+--------+

ТАБЛИЦА ТЕРМИНОВ

+-------+-----+
|term_id|name |
+-------+-----+
|1      |500mg|
+-------+-----+
|2      |10   |
+-------+-----+

QUERY

SELECT 
  products.id,
  products.name,
  price.value AS price,
  sku.value AS sku,
  size.name AS size
FROM products

/* These joins are performing quickly */

INNER JOIN `metadata` AS price ON products.id = price.product_id AND price.meta_key = 'price'
INNER JOIN `metadata` AS sku ON products.id = sku.product_id AND sku.meta_key = 'sku'

/* Here the part that is really slowing it down - I run this chunk about 5 times with different strings to match */

INNER JOIN `term_relationships` AS tr ON products.id = tr.object_id
  INNER JOIN `term_taxonomy` AS tt
  ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
    INNER JOIN `terms` AS size
    ON tt.term_id = size.term_id

Ответы

Ответ 1

Вероятность вашей производительности, скорее всего, вызвана соединением с таблицей "term_taxonomy".
Все остальные объединения, похоже, используют первичный ключ (где у вас есть вероятные рабочие индексы). Итак, мое предложение состоит в том, чтобы добавить составной индекс в term_taxonomy_id и term_id (или если вы должны: таксономия). Вот так:

CREATE UNIQUE INDEX idx_term_taxonomy_id_taxonomy
ON term_taxonomy( term_taxonomy_id, taxonomy);

Надеюсь, это поможет вам.

Ответ 2

Убедитесь, что все столбцы, на которых есть "ON" условные операторы, должны быть проиндексированы. Это значительно улучшит скорость.

Ответ 3

Попробуйте следующее:

SELECT p.id, p.name, MAX(CASE m.meta_key WHEN 'price' THEN m.value ELSE '' END) AS price, 
       MAX(CASE m.meta_key WHEN 'sku' THEN m.value ELSE '' END) AS sku, s.name AS size
FROM products p 
INNER JOIN `metadata` AS m ON p.id = m.product_id  
INNER JOIN `term_relationships` AS tr ON p.id = tr.object_id 
INNER JOIN `term_taxonomy` AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'
INNER JOIN `terms` AS s ON tt.term_id = s.term_id
GROUP BY p.id;

Если вы все еще обнаруживаете, что ваш запрос медленный, добавьте план EXPLAIN моего запроса, чтобы я мог найти, какие столбцы нужны INDEX.

Ответ 4

    Declare @query as NVARCHAR(MAX)
    set @query = ('SELECT 
    products.id,
    products.name,
    price.value AS price,
    sku.value AS sku,
    size.name AS size
    FROM products
    INNER JOIN metadata AS price ON products.id = price.product_id AND price.meta_key = price
    INNER JOIN metadata AS sku ON products.id = sku.product_id AND sku.meta_key = sku
    INNER JOIN term_relationships AS tr ON products.id = tr.object_id
    INNER JOIN term_taxonomy AS tt
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = size
    INNER JOIN terms AS size
    ON tt.term_id = size.term_id
    into #t')

    exec(@query);
    select * from #t

Я надеюсь, что вышеупомянутый способ уменьшит использование времени или создаст временную таблицу со всеми выбранными вами полями и обновит временную таблицу, присоединившись к временной таблице ко всем другим таблицам, также может быть эффективным, ну, я не уверенный об этом, но даже я жду вашего результата, поскольку ваш вопрос кажется интересным.

Ответ 5

Ниже script отформатирован в соответствии с правилами SQL Server. Вы можете изменить это в соответствии с правилами MySQL и попробовать его -

SELECT 
  P.id,
  P.name,
  PIVOT_METADATA.price,
  PIVOT_METADATA.sku,
  size.name AS size
FROM products P (NOLOCK)

INNER JOIN term_relationships AS tr (NOLOCK)
    ON P.id = tr.object_id

INNER JOIN term_taxonomy AS tt (NOLOCK)
    ON tr.term_taxonomy_id = tt.term_taxonomy_id AND tt.taxonomy = 'size'

INNER JOIN terms AS size (NOLOCK)
    ON tt.term_id = size.term_id

INNER JOIN METADATA (NOLOCK)
    PIVOT
    (
        MAX(value)
        FOR [meta_key] IN (price,sku)
    )AS PIVOT_METADATA
    ON P.id = PIVOT_METADATA.product_id

То, что я чувствую, может быть узким местом в вашем запросе. Вы присоединяетесь к метаданным 2 раза. Поскольку в ваших таблицах есть отношения "один-ко-многим", метаданные 2-join не пострадают, но после этого, когда вы присоединяетесь к большему количеству таблиц, количество строк из-за увеличения отношения "один ко многим" - и, следовательно, падение производительности.

Что я пытался достичь. Я убеждаюсь, что как можно больше взаимосвязей 1 к 1. Чтобы сделать это, я сделал Pivot по метаданным и цене и ску в качестве столбцов. Теперь мой идентификатор продукта должен иметь только одну строку в метаданных. alos, я убедился, что присоединяюсь к этому пикоту в самом конце.

Попробуй. Пожалуйста, поделитесь ожидаемой производительностью, количеством записей, которые у вас есть, а также тем, что вы получаете с моим asnwer.

Ответ 6

METADATA_TABLE и TERM_RELATIONSHIP_TABLE не имеют прокси-ключа. Когда в этих таблицах будут огромные записи, ваша производительность запроса будет удалена.

Контрольные точки для повышения производительности.

  • Все таблицы должны иметь первичный ключ. Это связано с тем, что строки в таблице будут физически отсортированы.
  • Для маленьких и запросов, содержащих несколько таблиц, содержащих первичный ключ в таблице, будет достаточно. Если вы по-прежнему хотите повысить производительность, создайте некластеризованный индекс для столбцов, например поле * object_Id таблицы term_relationships *. Некластеризованный индекс должен быть создан для тех столбцов в таблице, которые участвуют в операции объединения.

Однако следует отметить, что некластеризованный индекс должен быть очень меньше в тех таблицах, где происходит несколько вложений и обновлений. Это не простой вопрос, и на него нельзя ответить только в зависимости от времени выполнения. Существуют и другие факторы, которые влияют на ответ, особенно если среда, в которой выполняется хранимая процедура, в значительной степени является транзакционной.

Здесь вы можете найти

Ответ 7

Я бы предложил те:

  • Рассмотрите возможность сокращения этих объединений с уровня бизнеса;
  • Если это невозможно сделать из "верхнего" (бизнес-уровня), а данные не для реального времени, я бы предложил подготовить таблицу памяти (я знаю, что решение не идеально). И непосредственно выберите данные из таблицы памяти.

По моему опыту:

  • "joins" - это убийца для производительности, чем больше ваши данные, тем больше боли вы почувствуете;
  • Попытайтесь избавиться от объединений, а не пытайтесь повысить производительность запросов, сохраняя соединения, если только вам это не нужно. Обычно я попытаюсь исправить эти проблемы с "сверху" до "снизу"
  • Последнее предложение - если все выше не работает. Я буду рассматривать "map/reduce + fulltext search", если это стоит того сделать.

(Простите меня, я не предлагал решения для улучшения производительности вашего запроса.)