Как оптимизировать SQL-запрос, который использует GROUP BY и объединяет таблицы отношений «многие ко многим»? - PullRequest
0 голосов
/ 27 сентября 2019

У меня есть таблицы с отношением многие ко многим:

CREATE TABLE `item` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL DEFAULT '',
  `size_id` tinyint(3) NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
  INDEX `size` (`size_id`)
);

CREATE TABLE `items_styles` (
  `style_id` smallint(5) unsigned NOT NULL,
  `item_id` mediumint(8) unsigned NOT NULL,
  PRIMARY KEY (`item_id`, `style_id`),
  INDEX `style` (`style_id`),
  INDEX `item` (`item_id`),
  CONSTRAINT `items_styles_item_id_item_id` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`)
);

CREATE TABLE `items_themes` (
  `theme_id` tinyint(3) unsigned NOT NULL,
  `item_id` mediumint(8) unsigned NOT NULL,
  PRIMARY KEY (`item_id`, `theme_id`),
  INDEX `theme` (`theme_id`),
  INDEX `item` (`item_id`),
  CONSTRAINT `items_themes_item_id_item_id` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`)
);

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

SELECT i_s.style_id, COUNT(i.id) total FROM item i
JOIN items_themes i_t ON i.id = i_t.item_id AND i_t.theme_id IN (6, 7)
JOIN items_styles i_s ON i.id = i_s.item_id
GROUP BY i_s.style_id;

-- or like this
SELECT i_s.style_id, COUNT(i.id) total FROM item i
JOIN items_themes i_t ON i.id = i_t.item_id AND i_t.theme_id IN (6, 7)
JOIN items_styles i_s ON i.id = i_s.item_id
WHERE i.size_id != 3
GROUP BY i_s.style_id;

enter image description here

enter image description here

Проблема в том, что таблицы довольно большие, поэтому выполнение запросов занимает много времени (~ 8 секунд)

item - 8M строк items_styles - 12M строк items_themes - 11M строк

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

1 Ответ

1 голос
/ 27 сентября 2019

Во-первых, вам не нужна таблица items для запросов.Вероятно, не сильно влияет на производительность, но в этом нет необходимости.

Таким образом, вы можете написать запрос в виде:

SELECT i_s.style_id, COUNT(*) as total
FROM items_themes i_t JOIN
     items_styles i_s 
     ON i_s.item_id = i_t.item_id
WHERE i_t.theme_id IN (6, 7)
GROUP BY i_s.style_id;

Для этого запроса вы хотите индекс на items_themes(theme_id, item_id).Вы ничего не можете поделать с GROUP BY.

Тогда, я не думаю, что это то, чего вы действительно хотите, потому что это удвоит счет предмета, который имеет обе темы.Поэтому вместо этого используйте EXISTS:

SELECT i_s.style_id, COUNT(*) as total
FROM items_styles i_s          
WHERE EXISTS (SELECT 
              FROM items_themes i_t
              WHERE i_t.item_id = i_s.item_id AND
                    i_t.theme_id IN (6, 7)
             )
GROUP BY i_s.style_id;

. Для этого вам нужен индекс на items_themes(item_id, theme_id).Вы также можете попробовать индекс на items_styles(style_id).Некоторые базы данных могли бы использовать эту, но я предполагаю, что не MariaDB.

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