it-swarm.com.ru

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

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

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

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

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

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

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

METADATA TABLE

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

TERM_RELATIONSHIPS TABLE

+---------+----------------+
|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's 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
17
dloewen

Скорее всего, ваша проблема с производительностью связана с объединением с таблицей term_taxonomy. 
Кажется, что все другие объединения используют первичный ключ (где вы, вероятно, работаете с индексами). 

Поэтому я предлагаю добавить составной индекс в term_taxonomy_id и term_id (или, если вам необходимо: таксономия ). Как это:

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

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

10
carleson

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

2
akkig

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

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

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

  • «соединения» снижают производительность, чем больше ваши данные, тем больше боли вы почувствуете; 
  • Старайтесь избавляться от объединений, а не пытайтесь повысить производительность запросов, сохраняя объединения, если в этом нет необходимости. Обычно я пытаюсь исправить эти проблемы сверху вниз
  • Последнее предложение, если все вышеперечисленное не работает. Я рассмотрю «поиск по карте/уменьшить + полнотекстовый поиск», если это того стоит.

(Извините, я не предоставил решение для повышения производительности вашего запроса.) 

0
Joshua

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

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

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

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

Вы можете найти больше здесь

0
gokul
    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

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

0
JB9

Приведенный ниже скрипт отформатирован в соответствии с правилами 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 раза. Так как в ваших таблицах есть отношения 1-ко-многим, 2-объединение метаданных не повредит, но после этого, когда вы присоединяете больше таблиц - количество строк из-за увеличения отношения 1-ко-многим - и, следовательно, снижается производительность ,.

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

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

0
Suyash Khandwe

Попробуй это: 

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.

0
Saharsh Shah