Оптимизируйте этот SQL-запрос
Этот SQL-запрос вызывает у меня отвращение. Я этого не писал, но это серьезная причина проблем на наших серверах. Я готов разделить его на несколько запросов и выполнить некоторую обработку с помощью PHP (например, RAND()).
$sql = "SELECT a.code, a.ad_id, a.position, a.type, a.image, a.url, a.height, a.width
FROM " . AD_TABLE ." a, " . USER_GROUP_TABLE . " g
WHERE (a.max_views >= a.views OR a.max_views = '0')
AND (FIND_IN_SET(" .$forum_id. ", a.show_forums) > 0 OR a.show_all_forums = '1')
AND g.user_id = " . $user->data['user_id'] . "
AND FIND_IN_SET(g.group_id, a.groups)
AND FIND_IN_SET(" . $user->data['user_rank'] . ", a.ranks)
AND a.start_time < " . time() . "
AND a.end_time > " . time() . "
AND (a.clicks <= a.max_clicks OR a.max_clicks = '0')
ORDER BY rand()";
Да, я чувствую себя отвратительно после вставки этого...
РЕДАКТИРОВАТЬ: Ниже приведены результаты "ОБЪЯСНЕНИЯ" по образцу запроса в приведенном выше формате, разделенные запятыми:
"id","select_type","table","type","possible_keys","key","key_len","ref","rows","Extra"
1,"SIMPLE","g","ref","user_id","user_id","3","const",6,"Using temporary; Using filesort"
1,"SIMPLE","a","ALL","max_views","","","",10,"Using where"
Это
4 answers
Здесь у вас есть три основные проблемы:
-
FIND_IN_SET
.Это невозможно, индекс не может сделать это быстрее. Создайте таблицу отношений "многие ко многим" (или таблицы).
a.start_time < GETDATE() AND a.end_time > GETDATE()
MySQL
это не очень хорошо для оптимизации. Вы можете сохранять временные интервалы в виде геометрических полей и создавать над нимиSPATIAL INDEX
, это будет намного быстрее (хотя и менее читабельно)ORDER BY RAND()
Если вы используете это для выборки данных (т.е. вам не нужно все строки, а скорее небольшое случайное подмножество), есть более эффективный способ сделать это, описанный в этой статье в моем блоге:
- У вас нет явного соединения в этом запросе между таблицей a и таблицей g: они связаны только с помощью find_in_set (g.group_id, a.группы)
- Насколько велик ваш набор в "a.группы", т.Е. Содержит ли строка csv в большинстве случаев один идентификатор группы?
- Если 99% случаев содержат только 1 группу, то сделайте цикл foreach над "a.groups" в php и выполните реальное объединение (или, возможно, это может полностью исключить вашу таблицу user_group_table) из запрос.
- В меньшинстве случаев, когда у вас более 1 члена группы, запрос все равно будет выполняться нормально с явным присоединением.
Это добавит еще немного кода в ваш класс/функцию php.
Я предполагаю, что ваша проблема связана с датами.
AND a.start_time < " . time() . "
AND a.end_time > " . time() . "
Я бы попробовал поместить индексы в эти поля и посмотреть, поможет ли это. Сравнение дат приводит к тому, что база данных сравнивает каждую строку в таблице.
Если вы часто выполняете это.. попробуйте использовать переменные привязки (вместо объединения строк).. чтобы запрос не нужно было анализировать каждый раз..
Редактировать: Извините.. не видел тег MYSQL. Если это не изменилось в последнее время, я не думаю, что MySQL любит подготовленные заявления. ОРАКУЛ, с другой стороны, ЛЮБИТ их.