tags:

views:

48

answers:

2
id  Subject   mark   Year
-------------------------
1   Maths      32    2008
1   Science    40    2009
1   Science    45    2008
1   English    50    2009
1   English    60    2008

I am looking for a result like this:

id  Maths   Science   English
-----------------------------
1   32      40 & 45   50 & 60

Please advise. I am using MySQL.

+1  A: 

For the first step, take a look at the group_concat() functiopn in mysql, it may be of some help

SELECT `id`,
       `subject`,
       group_concat( `mark`
                     ORDER BY `year` DESC
                     SEPARATOR ' & '
                   ) marks
  FROM `subjects` 
 GROUP BY `id`,
          `subject`

UPDATE Managed to get as far as

SELECT `id`,
       IF (`subject` = 'English', `marks`, NULL) AS English,
       IF (`subject` = 'Maths', `marks`, NULL) AS Maths,
       IF (`subject` = 'Science', `marks`, NULL) AS Science
  FROM ( SELECT `id`, 
                `subject`, 
                group_concat( `mark` 
                              ORDER BY `year` DESC 
                              SEPARATOR ' & ' 
                            ) marks 
           FROM `subjects`  
          GROUP BY `id`, 
                   `subject` 
         ) x
GROUP BY `id`,
         `subject` 

which returns

+----+---------+-------+---------+
| id | English | Maths | Science |
+----+---------+-------+---------+
|  1 | 50 & 60 | NULL  | NULL    |
|  1 | NULL    | 32    | NULL    |
|  1 | NULL    | NULL  | 40 & 45 |
+----+---------+-------+---------+
3 rows in set (0.01 sec)

but can't quite get that last grouping right to return it all as a single row. Don't know if anybody else can help me get the final step right when taking this approach to the problem.... ordinarily, I'd return the result to PHP "as is", and do the final grouping there; but a complete MySQL solution would be interesting.

Mark Baker
I need Mysql query only. Thanks for immediate reply
subakaran
hi Mark, Thanks for your reply. I think , I need to learn more about CONCAT() function from mysql. Thanks
subakaran
Thanks for your help. I got solution from Mr.Daniel. Thanks. "IF" condition is also good.
subakaran
+4  A: 

As @Mark suggested, GROUP_CONCAT() can give you the following result:

SELECT   id, subject, GROUP_CONCAT(mark SEPARATOR ' & ') marks
FROM     results 
GROUP BY id, subject;
+------+---------+---------+
| id   | subject | marks   |
+------+---------+---------+
|    1 | English | 50 & 60 |
|    1 | Maths   | 32      |
|    1 | Science | 40 & 45 |
+------+---------+---------+
3 rows in set (0.00 sec)

From this test case:

CREATE TABLE results (id int, subject varchar(10), mark int);

INSERT INTO results VALUES (1, 'Maths', 32);
INSERT INTO results VALUES (1, 'Science', 40);
INSERT INTO results VALUES (1, 'Science', 45);
INSERT INTO results VALUES (1, 'English', 50);
INSERT INTO results VALUES (1, 'English', 60);

However another way to tackle the problem would be by using as sub query for each subject:

SELECT    r.id,
          (SELECT   GROUP_CONCAT(r_eng.mark SEPARATOR ' & ')
           FROM     results r_eng
           WHERE    r_eng.subject = 'English' AND r_eng.id = r.id) English,
          (SELECT   GROUP_CONCAT(r_eng.mark SEPARATOR ' & ') 
           FROM     results r_eng
           WHERE    r_eng.subject = 'Maths' AND r_eng.id = r.id) Maths,
          (SELECT   GROUP_CONCAT(r_eng.mark SEPARATOR ' & ') 
           FROM     results r_eng
           WHERE    r_eng.subject = 'Science' AND r_eng.id = r.id) Science
FROM      results r
GROUP BY  r.id;

Which will give the following result:

+------+---------+-------+---------+
| id   | English | Maths | Science |
+------+---------+-------+---------+
|    1 | 50 & 60 | 32    | 40 & 45 |
+------+---------+-------+---------+
1 row in set (0.01 sec)

UPDATE:

Further to the comments, it looks like you need to take the year field in consideration. Luckily the GROUP_CONCAT() function takes an ORDER BY clause which we can use. Let's start from a new test case with the year field:

CREATE TABLE results (id int, subject varchar(10), mark int, year int);

INSERT INTO results VALUES (1, 'Maths', 32, 2008);
INSERT INTO results VALUES (1, 'Science', 40, 2009);
INSERT INTO results VALUES (1, 'Science', 45, 2008);
INSERT INTO results VALUES (1, 'English', 50, 2009);
INSERT INTO results VALUES (1, 'English', 60, 2008);

SELECT * FROM results;
+------+---------+------+------+
| id   | subject | mark | year |
+------+---------+------+------+
|    1 | Maths   |   32 | 2008 |
|    1 | Science |   40 | 2009 |
|    1 | Science |   45 | 2008 |
|    1 | English |   50 | 2009 |
|    1 | English |   60 | 2008 |
+------+---------+------+------+
5 rows in set (0.00 sec)

Then we would be able to use the GROUP_CONCAT() function with the ORDER BY clause as follows:

SELECT   id, 
         subject, 
         GROUP_CONCAT(mark ORDER BY year SEPARATOR ' & ') marks, 
         GROUP_CONCAT(year ORDER BY year SEPARATOR ' & ') years
FROM     results 
GROUP BY id, subject;

+------+---------+---------+-------------+
| id   | subject | marks   | years       |
+------+---------+---------+-------------+
|    1 | English | 60 & 50 | 2008 & 2009 |
|    1 | Maths   | 32      | 2008        |
|    1 | Science | 45 & 40 | 2008 & 2009 |
+------+---------+---------+-------------+
3 rows in set (0.00 sec)

Finally, to GROUP BY everything in one horizontal row, we can use the subquery technique that we used in the earlier example:

SELECT    r.id,
          (SELECT  GROUP_CONCAT(r_eng.mark ORDER BY year SEPARATOR ' & ') 
           FROM    results r_eng
           WHERE   r_eng.subject = 'English' AND r_eng.id = r.id) English,
          (SELECT  GROUP_CONCAT(r_eng.year ORDER BY year SEPARATOR ' & ')
            FROM   results r_eng
            WHERE  r_eng.subject = 'English' AND r_eng.id = r.id) Years_English,
          (SELECT  GROUP_CONCAT(r_eng.mark ORDER BY year SEPARATOR ' & ')
           FROM    results r_eng
           WHERE   r_eng.subject = 'Maths' AND r_eng.id = r.id) Maths,
          (SELECT  GROUP_CONCAT(r_eng.year ORDER BY year SEPARATOR ' & ')
            FROM   results r_eng
            WHERE  r_eng.subject = 'Maths' AND r_eng.id = r.id) Years_Maths,
          (SELECT  GROUP_CONCAT(r_eng.mark ORDER BY year SEPARATOR ' & ')
           FROM    results r_eng
           WHERE   r_eng.subject = 'Science' AND r_eng.id = r.id) Science,
          (SELECT  GROUP_CONCAT(r_eng.year ORDER BY year SEPARATOR ' & ')
            FROM   results r_eng
            WHERE  r_eng.subject = 'Science' AND r_eng.id = r.id) Years_Science
FROM      results r
GROUP BY  r.id;

Which will return the following:

+----+---------+---------------+-------+-------------+---------+---------------+
| id | English | Years_English | Maths | Years_Maths | Science | Years_Science |
+----+---------+---------------+-------+-------------+---------+---------------+
|  1 | 60 & 50 | 2008 & 2009   | 32    | 2008        | 45 & 40 | 2008 & 2009   |
+----+---------+---------------+-------+-------------+---------+---------------+
1 row in set (0.01 sec)

If you wanted the marks and the years ordered in descending order, you could simply add the DESC keyword after each ORDER BY year.

Daniel Vassallo
I need results based on year and then I want to display in one horizontal row.
subakaran
subakaran
@subakaran: What do you mean exactly by "based on year"? ... You mean that the earliest marks should appear first?
Daniel Vassallo
Hi daniel, Thank you so much. Just I check with you something.am using more than five tables with JOIN query and retrieve results like yours. Like your results, I want to display more than 300 row. so is it possible to display like this. now I cannot test. Because Project has in my office local server. how do you feel execute time. Then can you display year also.Thanks for your help.
subakaran
yes. correct . earliest will be appear on first
subakaran
I have four year results. so I want to generate report . So I need to show those who have scored how much marks in every subject based on year. I appreciate your help.
subakaran
pilcrow
@pilcrow: Well, I guess so :) ... Let me update... Fixed, much neater now... Thanks @pilcrow :)
Daniel Vassallo
subakaran
@subakaran: I'm glad this helped :) ... 300 rows is not a lot, and you'll have no performance problems for sure with that amount of rows.
Daniel Vassallo
Thanks for your adviceThanks. I keep touch with you.
subakaran
@subakaran: You may want to mark the answer as "accepted" by clicking on the green tick on the left, if it was helpful in solving your problem :)
Daniel Vassallo
ya I have done. This is the first time I used this forum. very helpful to me.
subakaran