I’m struggling to know where to start on this one. I want to write a SELECT query which will return all the students in a particular class and the subjects they each take. But the subjects are stored in rows of another table and I need to somehow make them become a new column for every subject.
I have four tables:
Class Groups:
ID | Class Name | Teacher |
---|---|---|
1 | Blue | Mr Smith |
2 | Red | Miss Brown |
Students:
ID | ClassID | Last Name | First Name |
---|---|---|---|
1 | 1 | Cooper | Sarah |
2 | 2 | Jones | John |
3 | 1 | Moody | Claire |
4 | 1 | Baggins | Frodo |
Classes:
ID | Class |
---|---|
1 | Geography |
2 | Science |
3 | English |
4 | History |
5 | Spanish |
Class_attendees
Class ID | Student_ID |
---|---|
1 | 1 |
1 | 2 |
1 | 4 |
2 | 1 |
2 | 5 |
3 | 2 |
3 | 3 |
3 | 4 |
4 | 1 |
4 | 2 |
4 | 3 |
I want to write a SELECT query that shows me the timetable for Blue Class that would look like this.
Last Name | First Name | Geography | Science | English | History |
---|---|---|---|---|---|
Cooper | Sarah | 1 | 1 | 1 | |
Moody | Claire | 1 | 1 | 1 | |
Baggins | Frodo | 1 | 1 | 1 |
Since no-one in Blue Class is taking Spanish I don’t need a column for it.
2
Answers
The first part is a straightforward
JOIN
– but the second part ("…make them become a new column" and "shows me the timetable for") is not possible in SQL nor is it supported by the relational-model that SQL is based on, because the "timetable" view you describe is not an example of relational-data (btw, the term "relational" does not refer to "relationships" or foreign-keys; please see Codd’s paper I linked to).One of the fundamental rules here (paraphrased) is that columns are static (i.e. your SQL query defines the columns directly: e.g.
a
,b
,c
inSELECT a, b c FROM d
), so when dealing with data that may-or-may-not-exist (or instead, not-only exists, but has a very high cardinality) then that’s all in the rows/tuples.So your internal data representation of these undefined columns based on your not-yet-known-in-advance query results cannot have these dynamic columns, but that’s okay because the job of rendering a table/data-grid with dynamic columns is best done by your presentation layer (presumably PHP to generate a HTML
<table>
or some Report/templating engine you’re using?) – but not using only SQL.(Yes, you can hack it to work by using Dynamic SQL but let’s just not go there. There is also
PIVOT
/UNPIVOT
, but that still requires you to know what the columns are in-advance when you write the query).Anyway, the first part is easily done:
…which will give you this:
…and as said, the task of flattening this into that "timetable" view will be done by your presentation-layer and cannot be done using SQL.
…but if you don’t care about de jure columns, you can still flatten the results using
GROUP_CONCAT
:Gives you…
While MySQL has no PIVOT function, you could have make use of
CASE WHEN – THEN in this way: (code not tested)
Class_id column in table Students has same name of Class ID in Class_attendees table but two different meanings, you should choose better naming.
Info in Classes tables is replicated in CASE WHEN instructions