Обтекание столбцов в функциях помешает MySQL эффективно использовать индекс.
WHERE YEAR(c.dataCupo) = ...
AND MONTH(c.dataCupo) = ...
Вот шаблон, который позволяет MySQL использовать операцию сканирования диапазона для соответствующего индекса
WHERE c.dataCupo >= ...
AND c.dataCupo < ...
Коррелированный подзапрос в списке SELECT будет выполнен несколько раз. Если мы собираемся пойти по этому пути, то было бы намного лучше получить группировку fechas
done ПЕРВЫМ, а затем выполнять подзапрос для каждого месяца, сокращая количество выполнений.
Но я бы этого не сделал, я бы использовал операцию внешнего соединения.
И я получал бы только одну строку в месяц из 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'
получает нас только в первый день месяца. Похоже, что заполнение временной таблицы дает нам уникальные значения даты, поэтому мы не получим дубликатов.