Оптимизировать запрос, использующий несколько левых объединений для одних и тех же таблиц - PullRequest
4 голосов
/ 11 октября 2010

Я столкнулся с запросом, который занимает "слишком долго". Запрос имеет более 50 левых соединений между 10 или около того таблицами. Чтобы дать краткий обзор модели базы данных, объединенные таблицы представляют собой таблицы, в которых хранятся данные для определенного типа данных (например, date_fields, integer_fields, text_fields и т. Д.), И у каждой есть столбец для значения, идентификатор «datafield», и идентификатор билета. Запрос строится программно на основе таблицы связи между «билетом» и его «полями данных».

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

...FROM tickets t
LEFT JOIN ticket_text_fields t001 ON(t.id=t001.ticket_id AND t001.textfield_id=7)
...
LEFT JOIN ticket_date_fields t056 ON(t.id=t056.ticket_id AND t056.datafield_id=434)

При использовании объяснения на запрос выводится следующее:

1   SIMPLE   t       ref   idx_dataset_id                   idx_dataset_id  5   const   2871   Using where; Using temporary; Using filesort
1   SIMPLE   t001   ref   idx_ticket_id,idx_datafield_id   idx_ticket_id   5   t.id   5   
... 
1   SIMPLE   t056   ref   idx_ticket_id,idx_datafield_id   idx_ticket_id   5   t.id   8

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

Ответы [ 2 ]

7 голосов
/ 11 октября 2010

Вы используете разновидность ужасного antipattern , называемого Entity-Attribute-Value. Вы храните атрибуты в отдельных строках, поэтому, если вы хотите восстановить что-то, похожее на обычный ряд данных, вам нужно сделать одно соединение для каждого атрибута.

Не удивительно, что это создает запрос с 50 объединениями. Это слишком много для эффективной работы большинства баз данных (вы не определили, какую базу данных вы используете). В конце концов вам понадобится еще несколько атрибутов, и вы можете превысить архитектурный лимит базы данных по числу соединений, которые она может выполнить.

Решение: не восстанавливайте строку в SQL.

Вместо этого запрашивайте атрибуты в виде нескольких строк, а не пытайтесь объединить их в одну строку.

SELECT ... FROM tickets t
INNER JOIN ticket_text_fields f ON t.id=f.ticket_id
WHERE f.textfield_id IN (7, 8, 9, ...)
UNION ALL
SELECT ... FROM tickets t
INNER JOIN ticket_date_fields d ON t.id=d.ticket_id
WHERE d.datafield_id IN (434, 435, 436, ...)

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

0 голосов
/ 10 ноября 2015

для более ясного запроса я бы использовал что-то вроде этого:

SELECT ... FROM tickets as t  
JOIN ticket_text_fields as txt ON t.id = txt.ticket_id  
JOIN ticket_date_fields as dt ON t.id = dt.ticket_id  
WHERE txt.textfield_id IN (...)
AND dt.datefield_id IN (...)

Объединения, вероятно, будут левыми, но это зависит от структуры ваших данных.
В запросе нет объединения, и есть только два объединения

...