Мне нужно ускорить запрос в mysql, который занимает 45 секунд, мне нужно подсчитывать месяцы/годы, включая месяцы, в которых нет результатов в таблице, для чего я создаю временную таблицу дат, а затем запускаю следующий запрос, как я могу ускорить его?
//CREATE TABLE OF DATES AND INSERT
CREATE TEMPORARY TABLE fechas(fecha date);
INSERT INTO fechas(fecha)
select * from
(select adddate("1970-01-01",t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where selected_date between "2017-01-01" and "2018-01-01"
order by selected_date;
//QUERY
SELECT DATE_FORMAT(f.fecha, "%m-%Y") as data,
ifnull((
SELECT ifnull(COUNT(*),0)
FROM cupons c
WHERE YEAR(c.dataCupo) = YEAR(f.fecha)
AND MONTH(c.dataCupo) = MONTH(f.fecha)
and c.empresa=1
AND c.proveidor!="VINCULADO"
group by YEAR(c.dataCupo), MONTH(c.dataCupo)
ORDER BY c.dataCupo
),0) AS num
FROM fechas f
where f.fecha BETWEEN "2017-01-01" and "2018-01-01"
GROUP BY YEAR(f.fecha),MONTH(f.fecha);
//Результат:
данные | Num
01-2018 | 15
02-2018 | 0
03-2018 | 20
04-2018 | 0
....
Спасибо за ваше время.
Обтекание столбцов функциями предотвратит использование MySQL индекса.
WHERE YEAR(c.dataCupo) = ...
AND MONTH(c.dataCupo) = ...
Здесь шаблон, который позволяет MySQL использовать операцию сканирования диапазона по соответствующему индексу
WHERE c.dataCupo >= ...
AND c.dataCupo < ...
Коррелированный подзапрос в списке SELECT будет выполняться несколько раз. Если мы собираемся идти по этому маршруту, то было бы намного лучше, если бы FIRST fechas
группу fechas
, а затем выполнил подзапрос за каждый месяц, fechas
количество казней.
Но я бы этого не сделал, я бы использовал операцию внешнего соединения.
И я получаю только одну строку в месяц от fechas
и получаю соответствующие подсчеты в месяц, вместо того, чтобы получать их днем.
Без изменений во временную таблицу, то что-то вроде этого:
SELECT DATE_FORMAT(f.fecha, "%m-%Y") AS data
, COUNT(c.dataCupo) AS num
FROM fechas f
LEFT
JOIN cupons c
ON c.dataCupo >= f.fecha
AND c.dataCuop < f.fecha + INTERVAL 1 MONTH
AND c.empresa = 1
AND c.proveidor != 'VINCULADO'
WHERE f.fecha >= '2017-01-01'
AND f.fecha < '2018-01-01'
AND DATE_FORMAT(f.fecha,'%d') = '01'
GROUP
BY f.fecha
Обратите внимание, что условие DATE_FORMAT(f.fecha,'%d') = '01'
дает нам только первый день месяца. Похоже, что заполнение временной таблицы дает нам уникальные значения даты, поэтому мы не будем получать дубликаты.
cupons
для каждого из 365 дней, возвращаемых из феч. Гораздо эффективнее использовать сканирование диапазона для каждого отдельного дня ... при условии равномерного распределения значений дат, сканирование диапазона для одного n каждого выполнения быстро устраняет 99,7% строк из рассмотрения.
select adddate("1970-01-01"
....selected_date between "2017-01-01"
если вы не измените1970-01-01
на2017-01-01
потому что2017-01-01
ваш дата начала, а не1970-01-01