Задавайте вопросы, мы ответим
Вы не зашли.
SELECT
c.id card,
b.name b_name,
b.document_url b_url,
ug.id_package,
o.id,
o.title,
o.description,
o.telephone,
o.publish_site,
o.date_create,
o.city adress,
s.domain site
FROM links l
JOIN `usage` u ON l.`id_usage` = u.`id`
JOIN `organizations` o ON l.`id_organization` = o.id
JOIN `cards` c ON o.id = c.`id_organization`
JOIN `usage_groups` ug ON o.id = ug.`id_organization`
JOIN `businesses` b ON b.id = l.id_business
JOIN `sites` s ON o.id = s.id_organization
LEFT JOIN `businesses` b1 ON l.`id_business` = b1.`id`
LEFT JOIN `businesses` b2 ON b2.`id` = b1.`id_parent`
LEFT JOIN `businesses` b3 ON b3.`id` = b2.`id_parent`
LEFT JOIN `businesses` b4 ON b4.`id` = b3.`id_parent`
WHERE o.publish = 'Y'
AND 189 IN (
l.`id_business`,
b1.`id_parent`,
b2.`id_parent`,
b3.`id_parent`,
b4.`id_parent`
)
GROUP BY ug.`id_organization`
ORDER BY ug.id_package DESC, o.date_create
LIMIT 100, 10
CREATE TABLE `organizations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_creator` int(10) unsigned DEFAULT NULL,
`id_region` int(10) unsigned DEFAULT NULL,
`id_business` int(10) unsigned DEFAULT NULL,
`title` varchar(255) NOT NULL DEFAULT '',
`inn` varchar(20) DEFAULT NULL,
`kpp` varchar(20) DEFAULT NULL,
`bank` varchar(255) DEFAULT NULL,
`bank_bik` varchar(20) DEFAULT NULL,
`bank_c_account` varchar(20) DEFAULT NULL,
`bank_account` varchar(20) DEFAULT NULL,
`date_create` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`date_disable` datetime DEFAULT NULL,
`disable_reason` varchar(255) DEFAULT NULL,
`description` text,
`city` varchar(255) DEFAULT NULL,
`publish` char(1) DEFAULT NULL,
`publish_site` char(1) DEFAULT NULL,
`logo` varchar(255) DEFAULT NULL,
`telephone` varchar(60) DEFAULT NULL,
`fax` varchar(20) DEFAULT NULL,
`site` varchar(255) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_organizations_region` (`id_region`),
KEY `FK_organizations_business` (`id_business`),
KEY `FK_organizations_creator` (`id_creator`),
KEY `publish` (`publish`),
KEY `date_create` (`date_create`),
CONSTRAINT `FK_organizations_business` FOREIGN KEY (`id_business`) REFERENCES `businesses` (`id`),
CONSTRAINT `FK_organizations_creator` FOREIGN KEY (`id_creator`) REFERENCES `users` (`id`),
CONSTRAINT `FK_organizations_region` FOREIGN KEY (`id_region`) REFERENCES `regions` (`id`)
) ENGINE=InnoDB DEFAULT;
CREATE TABLE `usage_groups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_package` int(10) unsigned NOT NULL DEFAULT '0',
`id_organization` int(10) unsigned NOT NULL DEFAULT '0',
`date_create` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`note` varchar(255) DEFAULT NULL,
`card` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_usage_group_package` (`id_package`),
KEY `FK_usage_group_organiuzation` (`id_organization`),
KEY `id_package` (`id_package`,`id_organization`),
CONSTRAINT `FK_usage_group_organization` FOREIGN KEY (`id_organization`) REFERENCES `organizations` (`id`),
CONSTRAINT `FK_usage_group_package` FOREIGN KEY (`id_package`) REFERENCES `packages` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `cards` (
`id` int(8) unsigned zerofill NOT NULL AUTO_INCREMENT,
`id_series` int(10) unsigned NOT NULL DEFAULT '0',
`id_organization` int(10) unsigned DEFAULT NULL,
`id_package` int(10) unsigned NOT NULL DEFAULT '0',
`date_pay` datetime DEFAULT NULL,
`id_region` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_cards_series` (`id_series`),
KEY `FK_cards_organization` (`id_organization`),
KEY `FK_cards_package` (`id_package`),
KEY `id_region` (`id_region`),
CONSTRAINT `FK_cards_organization` FOREIGN KEY (`id_organization`) REFERENCES `organizations` (`id`),
CONSTRAINT `FK_cards_package` FOREIGN KEY (`id_package`) REFERENCES `packages` (`id`),
CONSTRAINT `FK_cards_series` FOREIGN KEY (`id_series`) REFERENCES `series` (`id`)
) ENGINE=InnoDB DEFAULT;
EXPLAIN:
id select_type table type key key_len ref rows Extra
1 SIMPLE c index FK_cards_organization 5 105400 Using index; Using temporary; Using filesort
1 SIMPLE o eq_ref PRIMARY 4 test.c.id_organization 1 Using where
1 SIMPLE ug ref FK_usage_group_organiuzation 4 test.o.id 1 Using where
1 SIMPLE l ref FK_links_organizations_id 4 test.c.id_organization 1
1 SIMPLE b1 eq_ref PRIMARY 4 test.l.id_business 1
1 SIMPLE b2 eq_ref PRIMARY 4 test.b1.id_parent 1
1 SIMPLE b3 eq_ref PRIMARY 4 test.b2.id_parent 1
1 SIMPLE b4 eq_ref PRIMARY 4 test.b3.id_parent 1 Using where
1 SIMPLE s ref FK_sites_organization 5 test.o.id 1 Using where
1 SIMPLE u eq_ref PRIMARY 4 test.l.id_usage 1 Using index
1 SIMPLE b eq_ref PRIMARY 4 test.l.id_business 1
Время выполнения 0.20 сек. Хочется избавиться от filesort и temporary.
Неактивен
Какая жуть
1. Насколько я вижу, b1 тождественно равно b в этом запросе => -1 таблица.
2. Время выполнения не такое уж и плохое для такого запроса, Вы просто хотите
избавиться от страшных слов, или боитесь, что упретесь в производительность
на 10 миллионах карточек?
3. Боюсь, если Вы хотите сделать это быстрее - прийдется перепродумать структуру.
Кусок типа "189 in (...)" никогда не будет использовать индекс.
Неактивен
paulus написал:
Какая жуть
1. Насколько я вижу, b1 тождественно равно b в этом запросе => -1 таблица.
Убрал b1.
На скорость никак не повлияло, или малозаметно. Хотя она и правда лишняя.
paulus написал:
2. Время выполнения не такое уж и плохое для такого запроса, Вы просто хотите
избавиться от страшных слов, или боитесь, что упретесь в производительность
на 10 миллионах карточек?
Размер запроса и страшные слова не волнуют - главное скорость!
paulus написал:
3. Боюсь, если Вы хотите сделать это быстрее - прийдется перепродумать структуру.
Кусок типа "189 in (...)" никогда не будет использовать индекс.
По-моему они все таки используются (обратите внимание на eq_ref):
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE b eq_ref PRIMARY PRIMARY 4 test.l.id_business 1
1 SIMPLE b2 eq_ref PRIMARY PRIMARY 4 test.b.id_parent 1
1 SIMPLE b3 eq_ref PRIMARY PRIMARY 4 test.b2.id_parent 1
1 SIMPLE b4 eq_ref PRIMARY PRIMARY 4 test.b3.id_parent 1 Using where
Перенес позиции таблиц в запросе - ug на 1е место, и создал индекс для с (id_organization, id)
SELECT
c.id card,
b.name b_name,
b.document_url b_url,
ug.id_package,
o.id,
o.title,
o.description,
o.telephone,
o.publish_site,
o.date_create,
o.city adress,
s.domain site
FROM `usage_groups` ug
JOIN `organizations` o ON ug.`id_organization` = o.id
JOIN `cards` c ON ug.`id_organization` = c.`id_organization`
JOIN links l ON o.id = l.`id_organization`
JOIN `usage` u ON l.`id_usage` = u.`id`
JOIN `businesses` b ON b.id = l.id_business
JOIN `sites` s ON o.id = s.id_organization
LEFT JOIN `businesses` b2 ON b2.`id` = b.`id_parent`
LEFT JOIN `businesses` b3 ON b3.`id` = b2.`id_parent`
LEFT JOIN `businesses` b4 ON b4.`id` = b3.`id_parent`
WHERE o.publish = 'Y'
AND 189 IN (
l.`id_business`,
b.`id_parent`,
b2.`id_parent`,
b3.`id_parent`,
b4.`id_parent`
)
GROUP BY ug.`id_organization`
ORDER BY ug.id_package DESC, o.date_create
LIMIT 100, 10
EXPLAIN стал лучше - вместо 106000 перебирается 1200 - используется меньшая таблица ug:
id select_type table type key key_len ref rows Extra
1 SIMPLE o ref publish 4 const 1290 Using where; Using temporary; Using filesort
1 SIMPLE ug ref FK_usage_group_organiuzation 4 test.o.id 1
1 SIMPLE l ref FK_links_organizations_id 4 test.o.id 1
1 SIMPLE b eq_ref PRIMARY 4 test.l.id_business 1
1 SIMPLE b2 eq_ref PRIMARY 4 test.b.id_parent 1
1 SIMPLE b3 eq_ref PRIMARY 4 test.b2.id_parent 1
1 SIMPLE b4 eq_ref PRIMARY 4 test.b3.id_parent 1 Using where
1 SIMPLE s ref FK_sites_organization 5 test.l.id_organization 1 Using where
1 SIMPLE u eq_ref PRIMARY 4 test.l.id_usage 1 Using index
1 SIMPLE c ref FK_cards_organization 5 test.ug.id_organization 7 Using where; Using index
Время выполнения уменьшилось на 0.05 сек, но filesort остался и все равно медленно?
Можно ли еще ускорить?
Неактивен
eq_ref - это хороший способ составления таблиц в JOIN. Он имеет отношение
к JOIN `usage` u ON l.`id_usage` = u.`id`, например. Но он не имеет отношения
к фильтрации данных, которые есть 189 in (...) и вследствие которого есть
"using where" справа от нескольких колонок.
Попробуйте переписать его в виде
WHERE b.id_parent = 189 OR ...
При этом после добавления на b ключика на (id_parent, id) должно летать.
Неактивен
paulus написал:
eq_ref - это хороший способ составления таблиц в JOIN. Он имеет отношение
к JOIN `usage` u ON l.`id_usage` = u.`id`, например. Но он не имеет отношения
к фильтрации данных, которые есть 189 in (...) и вследствие которого есть
"using where" справа от нескольких колонок.
Попробуйте переписать его в виде
WHERE b.id_parent = 189 OR ...
При этом после добавления на b ключика на (id_parent, id) должно летать.
Вы будете удивлены, но использование:
WHERE o.publish = 'Y'
AND (
l.`id_business` = 189
OR b.`id_parent` = 189
OR b2.`id_parent` = 189
OR b3.`id_parent` = 189
OR b4.`id_parent` = 189
)
никак не повлияло на скорость, а ключик на b (id_parent, id) никак не повлиял на скорость и даже никак не изменил EXPLAIN.
Хотя в теории я с вами согласен. Может быть новая версия MySQL стала использовать индексы на IN выражениях..
0.16 сек предел на свободном сервере, а на рабочем 1.00 - 5.00 сек
Есть ли смысл оптимизировать дальше? Или копаться в настройках сервера MySQL ?
Неактивен
Более того, если убрать все b - b4 и условия с ними связанные, то на производительсность это не отражается. Мне кажется ключ лежит в ORDER BY, который использует filesort.
Неактивен
Оптимизатор решил, что INDEX_MERGE в данном случае будет хуже FTS. Возможно,
он прав
Можете сделать табличку "предки бизнеса", где держать всех предков каждого
бизнеса (не только прямых родителей). Тогда будет один INNER JOIN, который
точно пробивает по индексу.
P.S. MySQL умеет использовать индексы в случае с IN, когда Вы пишете
column IN (value list), но не в обратном случае.
Неактивен
А в случае без b причина может быть другой. Например, Вы достаете почти
все данные из таблицы. При этом у Вас сортировка заведомо происходит не
по индексу, т.к. сортируете Вы по двум разным таблицам.
Есть шанс использовать сортировку по ключу для первого прохода.
Можете попробовать поиграть с JOIN, вытаскивая их в заданном Вами порядке
(SELECT STRAIGHT_JOIN ...). Если вытаскивать сначала из ug и объединять
все остальные таблицы через eq_ref, то MySQL может использовать индекс по
ug (который, разумеется, должен быть).
Неактивен
Еще интересный факт - чистая выборка всех в данном дереве занимает 0.02 сек. Т.е. надо наверное выдвинуть ее наперед.
SELECT
b.name b_name
FROM
`businesses` b
LEFT JOIN `businesses` b2 ON b2.`id` = b.`id_parent`
LEFT JOIN `businesses` b3 ON b3.`id` = b2.`id_parent`
LEFT JOIN `businesses` b4 ON b4.`id` = b3.`id_parent`
WHERE (
b.id = 189
OR b.`id_parent` = 189
OR b2.`id_parent` = 189
OR b3.`id_parent` = 189
OR b4.`id_parent` = 189
)
LIMIT 100, 10
10 rows fetched (0,02 sec)
Я плохо знаю как пользоваться STRAIGHT_JOIN Буду пробовать..
Неактивен
Просто пишете STRAIGHT_JOIN после SELECT - и таблицы будут собираться
в том порядке, который указываете Вы, а не в том, который выбирает оптимизатор.
Иногда он может ошибаться...
Неактивен
paulus написал:
Просто пишете STRAIGHT_JOIN после SELECT - и таблицы будут собираться
в том порядке, который указываете Вы, а не в том, который выбирает оптимизатор.
Иногда он может ошибаться...
Выигрыш 0.04 секунды на лицо - спасибо.
Наверное это предел 0.11 сек для такого громоздкого запроса. Или еще остались способы?
Неактивен
Способ - переделать сами данные. Сделайте таблицу, все-таки, с предками. Один JOIN
будет работать с индексом при ограничении WHERE, сейчас у Вас все равно выбираются
все строки, а потом идет поиск по этой временной табличке.
Неактивен
paulus написал:
Способ - переделать сами данные. Сделайте таблицу, все-таки, с предками. Один JOIN
будет работать с индексом при ограничении WHERE, сейчас у Вас все равно выбираются
все строки, а потом идет поиск по этой временной табличке.
Это я к сожалению сделать не смогу - не в моей компетенции изменение структуры таблиц. С деревом завязано много другого.
STRAIGHT_JOIN - отличная штука!
Неактивен
Тогда посмотрите еще на значение tmp_table_size. Если оно достаточно большое,
то временные таблицы будут создаваться в памяти, что несколько ускорит Ваши
запросы (я имею в виду production-машинку).
Неактивен
paulus написал:
Тогда посмотрите еще на значение tmp_table_size. Если оно достаточно большое,
то временные таблицы будут создаваться в памяти, что несколько ускорит Ваши
запросы (я имею в виду production-машинку).
Всего на машинке 512М, а tmp_table_size=32М. Есть ли смысл увеличивать?
Неактивен
Не имеет, разумеется. Осторожно с большим количеством таких запросов, а то из памяти очень
быстро выбьетесь.
Неактивен