#php #mysql #performance
#php #mysql #Производительность
Вопрос:
Верно, я понятия не имею, почему, но выполнение этого запроса занимает более 6 секунд, все индексы настроены правильно, и если я запускаю каждый запрос отдельно, он отлично работает менее чем за 0,5 секунды для выполнения.
Вот запрос
SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name,
(SELECT COUNT(*)
FROM supplier_questions q1
WHERE c.supplier_id = q1.supplier_id AND q1.incomplete = '0') AS questions,
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated,
(SELECT COUNT(*)
FROM supplier_questions q2
WHERE c.supplier_id = q2.supplier_id AND q2.reviewed = '1') AS reviewed,
questapproved,
ss.supplier_no AS supplier_no
FROM suppliers c
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id
WHERE c.supplier_id != '0' AND ss.site_id = '2'
GROUP BY c.supplier_id
ORDER BY c.supplier_name ASC
LIMIT 0, 20
Результаты запроса Explain следующие
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY ss ref site_id,supplier_id site_id 4 const 1287 Using where; Using temporary; Using filesort
1 PRIMARY c eq_ref PRIMARY PRIMARY 4 ss.supplier_id 1
3 DEPENDENT SUBQUERY q2 ref supplier_id,reviewed reviewed 4 const 263 Using where
2 DEPENDENT SUBQUERY q1 ref supplier_id,incomplete incomplete 4 const 254 Using where
Причина, по которой есть запросы count, заключается в том, что мне нужно знать количество строк из этих таблиц, это невозможно сделать в другом запросе, поскольку результаты также должны быть отсортированы по этим значениям : (
Комментарии:
1. Можете ли вы подтвердить, что на c.supplier_name есть индекс? Каково объяснение, когда вы удаляете два зависимых подзапроса?
2. У supplier_name нет индекса, но при добавлении он не изменяется.
3. Какие индексы у вас есть в таблице
supplier_questions
?4. «это невозможно сделать в другом запросе, поскольку результаты также должны быть отсортированы по этим значениям» — но запрос, который вы опубликовали, не является.
5. Денис сортировка по генерируется php-скриптом, сортировка по может не отображаться в запросе, но это можно изменить
Ответ №1:
Как удар в темноте, это выполняется быстрее? (У меня нет mysql для проверки синтаксиса, поэтому простите любые незначительные ошибки, но вы могли бы уловить идею)
SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name, questions, reviewed
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated,
questapproved, ss.supplier_no AS supplier_no
FROM suppliers c
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id
inner join
(SELECT supplier_id, sum(if(incomplete='0',1,0)) as questions, sum(if(incomplete='1',1,0)) as reviewed FROM supplier_questions q1 group by supplier_id) as tmp
on c.supplier_id = tmp.supplier_id
WHERE c.supplier_id != '0' AND ss.site_id = '2'
GROUP BY c.supplier_id
ORDER BY c.supplier_name ASC LIMIT 0, 20
Комментарии:
1. Этого не произойдет, потому что в конечном итоге он объединит все таблицы вместе. Странная вещь в его плане запроса заключается в том, что он не извлекает 20 лучших строк, используя c.supplier_name и применяя к этому join / подзапросы.
2. Выполняется быстрее, но возвращает только 3 строки вместо возможных 1000 строк
3. Возможно, вам не нужно внутреннее объединение, вам может понадобиться просто объединение, поскольку очевидно, что тогда включаются только элементы в обеих таблицах в их нынешнем виде.. @Denis, это не помогает, мне просто интересно, будет ли иметь какое-либо существенное значение подсчет вопросов и отзывов один раз.
4. Поиск ошибок, возможно, вы в чем-то правы, просто проверяю результаты вашего запроса, слегка измененные с помощью соединения по левому краю, и он пока кажется правильным, 0,124 секунды для получения 925 строк и 0,156 для извлечения 1257 строк
5. Поиск ошибок, возможно, вы к чему-то пришли, запрос возвращается с нулевыми вопросами и проверяется, попробуйте добавить IFNULL () к запросу, но все равно получите нулевые значения, есть идеи?
Ответ №2:
FROM suppliers c
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id
WHERE c.supplier_id != '0' AND ss.site_id = '2'
GROUP BY c.supplier_id
ORDER BY c.supplier_name ASC
Поскольку автоматически сгенерированные первичные ключи никогда не равны 0 (если не допущена ошибка при проектировании большой базы данных), вы можете удалить предложение c.supplier_id != ‘0’.
ss.site_id = ‘2’ должно быть в условии объединения для удобства чтения.
Похоже, что это должно соответствовать только одной строке в таблице supplier_site для каждого поставщика (если это ваше обычное отношение 1-N адресов объектов, т. Е. вы выбираете второй адрес каждого поставщика, возможно, ‘2’ соответствует ‘адресу выставления счетов’ или чему-то еще), поэтому ГРУППА ПО c.supplier_id бесполезна. Если GROUP BY действительно что-то делает, то запрос неверен, поскольку столбцы «address», которые предположительно поступают из таблицы supplier_site, будут получены из случайной строки.
Итак, вот упрощенный FROM (КУДА ушло) :
FROM suppliers c
INNER JOIN supplier_site ss ON
(c.supplier_id = ss.supplier_id AND ss.site_id = '2')
ORDER BY c.supplier_name ASC
Я полагаю, у вас есть индекс на c.supplier_name, поэтому эта часть запроса должна быть очень быстрой.
Теперь попробуйте этот запрос :
SELECT a.*,
questapproved,
ss.supplier_no AS supplier_no,
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated,
sum( q.incomplete = '0') AS questions,
sum( q.reviewed = '1' ) AS reviewed
FROM
(
SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name
FROM suppliers c
INNER JOIN supplier_site ss ON
(c.supplier_id = ss.supplier_id AND ss.site_id = '2')
ORDER BY c.supplier_name ASC
LIMIT 0, 20
) a
LEFT JOIN supplier_questions q ON (q.supplier_id = c.supplier_id)
GROUP BY c.supplier_id
ORDER BY c.supplier_name;
Комментарии:
1. Сгенерировал ряд ошибок SQL, после перемещения questapprove, supplier_no и rated в запрос FROM, на который он жалуется на c.supplier_id — неизвестный столбец в предложении ON
2. Ваш запрос работает, и он намного быстрее, однако не может упорядочиваться по вопросам, поскольку по какой-то причине он не упорядочивается
Ответ №3:
Если вы удалите вложенные запросы, вы получите что-то вроде этого:
SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name,
COUNT(IF (q1.incomplete = '0', '0', null)) AS questions,
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated,
COUNT(IF (q1.reviewed = '1', '1', null)) AS reviewed,
questapproved,
ss.supplier_no AS supplier_no
FROM suppliers c
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id
LEFT OUTER JOIN supplier_questions q1 ON c.supplier_id = q1.supplier_id
WHERE c.supplier_id != '0' AND ss.site_id = '2'
GROUP BY c.supplier_id
ORDER BY c.supplier_name ASC
LIMIT 0, 20
У меня нет доступной базы данных MySQL, поэтому в моем SQL могут быть ошибки.
Идея состоит в том, чтобы удалить вложенные запросы и заменить их внешним соединением
и используйте IF только для подсчета соответствующих строк.
Ответ №4:
Я бы сначала попытался реструктурировать, предварительно запросив агрегированные данные по количеству вопросов поставщика и проверив ИХ ОДИН РАЗ. Затем присоединитесь к остальным деталям. Используя ключевое слово STRAIGHT_JOIN, он должен обрабатываться в отображаемом порядке. Сначала это будет предварительная агрегация и использование этого в качестве основы для обратного соединения с поставщиками, а затем с сайтами поставщиков. Внешняя группа by не требуется, поскольку она все равно основана на идентификаторе поставщика. Однако присоединение к supplier_sites (ваш ss.supplier_no) будет означать, что у поставщика более одного местоположения. Означает ли это, что столбцы адреса и активного статуса происходят из этой таблицы?
Должно ли объединение вопросов быть связано с конкретным поставщиком и соответствующим местоположением сайта или нет?
Кроме того, поскольку предварительный запрос содержит предложение WHERE для supplier_id != ‘0’, в дальнейшем это не требуется, поскольку это будет основой обычного соединения с другими таблицами, что исключает их из результирующего набора.
SELECT STRAIGHT_JOIN
PreAggregate.supplier_id,
PreAggregate.supplier_name,
address1,
address2,
address3,
address4,
suppliertype,
postcode,
contact_name,
PreAggregate.Questions,
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated,
PreAggregate.Reviewed,
questapproved,
ss.supplier_no AS supplier_no
FROM
(select
s1.Supplier_ID,
s1.Supplier_Name,
SUM( IF( q1.Incomplete = '0', 1, 0 )) Questions,
SUM( IF( q1.Reviewed = '1', 1, 0 )) Reviewed
from
suppliers s1
join supplier_questions q1
ON s1.supplier_id = q1.supplier_id
where
s1.supplier_id != '0'
group by
s1.Supplier_ID
ORDER BY
s1.supplier_name ASC ) PreAggregate
JOIN suppliers c
ON PreAggregate.Supplier_ID = c.Supplier_ID
JOIN supplier_site ss
ON PreAggregate.Supplier_ID = ss.supplier_id
AND ss.Site_ID = '2'
LIMIT 0, 20
Комментарии:
1. Один поставщик может предоставить нескольким сайтам пользователям, разрешающим доступ только к определенным сайтам, следовательно, supplier_sites, что касается запроса, возвращает 0 строк
2. @Neo, я переместил ОГРАНИЧЕНИЕ 0,20 во ВНЕШНЮЮ часть запроса… Я предполагаю, что внутренний запрос ограничивался 20, из которых ни один из сайтов поставщиков, прошедших квалификацию, не имел site_id = ‘2’…