Вы можете перефразировать это как условное агрегирование:
SELECT DATE(r.created_at),
COUNT(DISTINCT CASE WHEN r.rating = 1 THEN r.user_id END) as raging_1,
COUNT(DISTINCT CASE WHEN r.rating = 2 THEN r.user_id END) as raging_2,
COUNT(DISTINCT CASE WHEN r.rating = 3 THEN r.user_id END) as raging_3,
COUNT(DISTINCT CASE WHEN r.rating = 4 THEN r.user_id END) as raging_4,
COUNT(DISTINCT CASE WHEN r.rating = 5 THEN r.user_id END) as raging_5
FROM ratings r
WHERE r.campaign_id = 2 AND
r.created_at >= '2018-08-01'
GROUP BY DATE(r.created_at);
COUNT(DISTINCT)
может быть дорогим. Удалите это, если можете.
В противном случае может быть быстрее сделать DISTINCT
один раз:
SELECT dte,
SUM( r.rating = 1 ) as raging_1,
SUM( r.rating = 2 ) as raging_2,
SUM( r.rating = 3 ) as raging_3,
SUM( r.rating = 4 ) as raging_4,
SUM( r.rating = 5 ) as raging_5
FROM (SELECT DISTINCT user_id, rating, DATE(r.created_at) as dte
FROM ratings r
WHERE r.campaign_id = 2 AND
r.created_at >= '2018-08-01'
) urd
GROUP BY dte;
Возвращает строки для каждого дня, который имеет хотя бы один рейтинг. Если в некоторые дни будут все нули, вам понадобится какое-то внешнее соединение. Это практически ничего не добавляет к производительности, поэтому ее можно использовать, если работает одно из перечисленных выше решений.