If you cannot change the table (keeping separate dates will help a little), then only indexes, and then:
Create a composite index: carrier, reg_date , then group carrier, reg_date and order reg_date, carrier .
You can create another index only for timestamps (it may work better for WHERE caluse, depending on your number of records outside the scope).
In addition, you can use fully unix timestamps and then insert this as a subquery, and the external can hide timestamps until human-readable dates (thus, the conversion is done after the group, and not for each individual record).
Creating Indexes:
CREATE INDEX bytime ON user_data (reg_date); CREATE INDEX daily_group ON user_data (carrier, reg_date);
Query:
SELECT FROM_UNIXTIME(reg_day, "%M %D %Y") AS reg_day , carrier , user_count FROM ( SELECT FLOOR(reg_date / (60 * 60 * 24)) AS reg_day , carrier , count(carrier) AS user_count FROM user_data WHERE reg_date >= UNIX_TIMESTAMP('2013-01-01 00:00:00') AND reg_date < UNIX_TIMESTAMP('2013-02-01 00:00:00') GROUP BY carrier, reg_day ORDER BY reg_day, carrier ) AS a;
source share