I have a query :
select score, count(1) as \'NumStudents\' from testresults where testid = \'mytestid\' group by score order by score
Just as a mental exercise I came up with this to generate a sequence in MySQL. As long as the number of tables in all databases on the box squared are less than the total length of the sequence it will work. I wouldn't recommend it for production though ;)
SELECT @n:=@n+1 as n from (select @n:=-1) x, Information_Schema.Tables y, Information_Schema.Tables WHERE @n<20; /* sequence from 0 to 20 inclusive */
Does MySQL support set-returning functions? Recent releases of PostgreSQL have a function, generate_series(start, stop)
that produces the value start
on the first row, start+1
on the second, and so on up to stop
on the stop
th row. The advantage of this is that you can put this function in a subselect in the FROM
clause and then join to it, instead of creating and populating a table and joining to that as suggested by le dorfier and Bill Karwin.
The most obvious way would be to create a table named "Scores" and left outer join your table to it.
SELECT s.score, COUNT(1) AS scoreCount
FROM score AS s
LEFT OUTER JOIN testScores AS ts
ON s.score = ts.score
GROUP BY s.score
If you don't want to create the table, you could use
SELECT
1 as score, SUM(CASE WHEN ts.score = 1 THEN 1 ELSE 0 END) AS scoreCount,
2 as score, SUM(CASE WHEN ts.score = 2 THEN 1 ELSE 0 END) AS scoreCount,
3 as score, SUM(CASE WHEN ts.score = 3 THEN 1 ELSE 0 END) AS scoreCount,
4 as score, SUM(CASE WHEN ts.score = 4 THEN 1 ELSE 0 END) AS scoreCount,
...
10 as score, SUM(CASE WHEN ts.score = 10 THEN 1 ELSE 0 END) AS scoreCount
FROM testScores AS ts
SQL is good at working with sets of data values in the database, but not so good at sets of data values that are not in the database.
The best workaround is to keep one small table for the values you need to range over:
CREATE TABLE ScoreValues (score int);
INSERT INTO ScoreValues (score)
VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), (10);
Given your comment that you define the max marks of a test in another table, you can join to that table in the following way, as long as ScoreValues
is sure to have values at least as high or higher than the greatest test's max marks:
SELECT v.score, COUNT(tr.score) AS 'NumStudents'
FROM ScoreValues v
JOIN Tests t ON (v.score <= t.maxmarks)
LEFT OUTER JOIN TestResults tr ON (v.score = tr.score AND t.testid = tr.testid)
WHERE t.testid = 'mytestid'
GROUP BY v.score;