Do you mean something like the following ( sqlfiddle )?
SELECT name, SUM(IF(A>=1,1,0)) as A, SUM(IF(B>=1,1,0)) as B, SUM(IF(C>=1,1,0)) as C FROM ( SELECT name, test_date, SUM(IF(score>79,1,0)) as A, SUM(IF(score BETWEEN 49 AND 79,1,0)) as B, SUM(IF(score<50,1,0)) as C FROM sometable GROUP BY name, test_date ) daygroups GROUP BY name
This first shuffles your data into the name, test_date, A, B, C lines in the subquery. Then the external query will aggregate these rows, taking 1 if that day there is at least one point of this class, otherwise it takes 0.
This should work too ( sqlfiddle ):
SELECT name, SUM(IF(lettergrade = 'A',1,0)) AS A, SUM(IF(lettergrade = 'B',1,0)) AS B, SUM(IF(lettergrade = 'C',1,0)) AS C FROM ( SELECT DISTINCT name, test_date, CASE WHEN score>79 THEN 'A' WHEN score BETWEEN 49 AND 79 THEN 'B' ELSE 'C' END AS lettergrade FROM sometable ) lettergrades GROUP BY name
I'm not sure which one will be better. This query uses DISTINCT , as you suggest in your question. First, it resolves each numerical score to the corresponding letter column, then DISTINCT displays duplicates. Finally, it shuffles the data into columns.