2016-03-31 2 views
1

У меня есть следующий запрос:Оптимизация запросов MySQL с диапазоном дат и присоединиться к

SELECT COUNT(*) 
    FROM datum d 

    JOIN datum_type dt 
    ON dt.datum_id = d.id 
    AND dt.type_id = '3' 

WHERE d.added_time >= DATE_FORMAT(CURDATE(), '%Y-%m') 
    AND d.added_time < DATE_FORMAT(CURDATE() + INTERVAL 1 MONTH, '%Y-%m') 

Есть индексы на d.id (Primary), d.added_time, dt.datum_id и dt.type_id

в настоящее время объяснить план:

+----+-------------+-------+--------+--------------------+---------+---------+-------------+--------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len |  ref  | rows | Extra | 
+----+-------------+-------+--------+--------------------+---------+---------+-------------+--------+-------------+ 
| 1 | SIMPLE  | dt | ref | type_id,datum_id | type_id |  1 | const  | 602628 |    | 
| 1 | SIMPLE  | d  | eq_ref | PRIMARY,added_time | PRIMARY |  8 | dt.datum_id |  1 | Using where | 
+----+-------------+-------+--------+--------------------+---------+---------+-------------+--------+-------------+ 

Как мы имеем базовые элементы записи в течение достаточно долгого времени, то, как представляется, присоединение к типу в первом использовании datum.id ГЛАВНЫЙ, а затем сканируя каждый присоединился ряд, чтобы увидеть, если нулевой точки. Добавлено_time is w в диапазоне.

Я попытался с помощью индекса added_time но план был объяснить:

+----+-------------+-------+-------+------------------+------------+---------+------+---------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows |   Extra   | 
+----+-------------+-------+-------+------------------+------------+---------+------+---------+--------------------------+ 
| 1 | SIMPLE  | d  | index | added_time  | added_time |  4 | NULL | 6195194 | Using where; Using index | 
| 1 | SIMPLE  | dt | ref | type_id,datum_id | datum_id |  8 | d.id |  1 | Using where    | 
+----+-------------+-------+-------+------------------+------------+---------+------+---------+--------------------------+ 

который занимает почти столько же времени, как есть так много datum_types различной datum_type.type_id в диапазоне datum.added_time.

Есть ли какая-то комбинация индекса, которая может ускорить это?

+0

Пробовал ли вы составной ключ (datum_id, type_id)? Я действительно не понимаю бит DATE_FORMAT, но, возможно, он мало влияет на производительность. – Strawberry

+0

Ооо, попробуй указатель. – Arth

+0

Ненужная нормализация 'datum_type'? –

ответ

1

Позвольте мне предположить, что added_time является datetime или date. Затем вы должны выразить условия как строки. Вместо этого используйте date константы:

SELECT COUNT(*) 
FROM datum d JOIN 
    datum_type dt 
    ON dt.datum_id = d.id AND 
     dt.type_id = '3' 
WHERE d.added_time >= DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY) AND 
     d.added_time < DATE_ADD(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY), INTERVAL 1 MONTH); 

Это может воспользоваться преимуществами индекса на datum(added_time, id) и datum_type(datum_id, type_id).

Если нет повторяющихся записей (для подсчета), поступающих от datum_type, я хотел бы предложить вам переписать запрос как:

SELECT COUNT(*) 
FROM datum d 
WHERE d.added_time >= DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY) AND 
     d.added_time < DATE_ADD(DATE_SUB(CURDATE(), INTERVAL DAY(CURDATE()) - 1 DAY), INTERVAL 1 MONTH) AND 
     EXISTS (SELECT 1 
       FROM datum_type dt 
       WHERE dt.datum_id = d.id AND dt.type_id = '3' 
      ); 

Если type_id является целым числом, то вы должны отбросить одиночные кавычки. Смешивание разных типов данных в SQL может путать оптимизацию и предотвращение использования индексов.

+0

Ваш диапазон дат не совпадает с моим, мне нужен текущий календарный месяц, и оптимизатор, похоже, не имеет проблемы. Я бы с удовольствием попробовал переписать его, если есть лучший способ, не используя 'DATE_FORMAT()'! Однако я попробую другие предложения. – Arth

+0

Мои извинения, ваш диапазон хорош, и это действительно решение, хотя я изменил его, чтобы использовать синтаксис 'date + INTERVAL expr unit'. Интересно изменить 'DATE_FORMAT (CURDATE(), '% Y-% m')' на 'DATE_FORMAT (CURDATE(), '% Y-% m-01')' и аналогичное изменение на другую строку имеет тот же эффект и также работал. – Arth

+0

Я считаю, что когда вы добавляете индекс в 'added_time', вы получаете' (добавлено_time, PRIMARY) 'бесплатно, поэтому мне это не нужно в конце. – Arth

Смежные вопросы