-1

I have a table that looks like this:

Column1 Column2
1       A
1       B
1       C
2       D
2       A
2       B
2       E
2       F
3       A
3       D
3       C
3       H
3       J

Is there a query in MySQL to display the information as below:

Column1   Header1    Header2    Header3   Header4   Header5
1         A          B          C       
2         D          A          B         E         F
3         A          D          C         H         J
user3918629
  • 13
  • 1
  • 7
  • 2
    what have you tried, please show us the code that you are having trouble with so we can help you improve it or enhance it. – Our Man in Bananas Aug 07 '14 at 13:49
  • 1
    @Philip To be fair they are asking whether a query exists that will do it, not for help with the query. – Arth Aug 07 '14 at 13:51
  • You cannot (legally) order the results in the way that you have suggested, because you have not made explicit (within your design) the fact that '2,D' precedes '2,A'... and why would you want them in separate columns? – Strawberry Aug 07 '14 at 14:11
  • @Strawberry The order does not particularly matter. I just need them displayed in separate columns because the table will need to be manipulated by other users (in Excel). Arth commented that unless I can order the data by As then Bs then Cs it would be easier; however I am unable to do so because the values in column2 vary quite a lot. – user3918629 Aug 07 '14 at 14:32
  • Do you know for sure that there won't be more than 5 values for row? – Strawberry Aug 07 '14 at 14:39

3 Answers3

5

this is possible... you will have to simulate a pivot table by faking it with MAX() and then a condition inside. you just have to know how many columns you want..

NOTE:

i just called the table letters since thats what it looks like from my position.

QUERY:

SELECT 
    id, 
    MAX(CASE counter WHEN 1 THEN letter ELSE ' ' END), 
    MAX(CASE counter WHEN 2 THEN letter ELSE ' ' END) ,
    MAX(CASE counter WHEN 3 THEN letter ELSE ' ' END) ,
    MAX(CASE counter WHEN 4 THEN letter ELSE ' ' END) ,
    MAX(CASE counter WHEN 5 THEN letter ELSE ' ' END) 
FROM
(   SELECT 
        id, letter, 
        IF(@A = id, @B := @B + 1, @B := 1) AS counter, 
        @A := id
    FROM letters
    CROSS JOIN(SELECT @A := 0, @B := 0) t
) temp
GROUP BY id;

so for this query i put a counter for the id.. counts the number of occurrences for the id... so then you just do MAX(CASE counter WHEN 1-5 THEN col2 ELSE ' ' END meaning when the counter is either 1 - 5 (could be more if you have more ids) put in the letter that corresponds to it or else put in a blank.. hope thats helpful!

RESULTS:

+----+------+-------+-------+-------+-------+
| ID | COL1 | COL2  | COL3  | COL4  | COL5  |
+----+------+-------+-------+-------+-------+
| 1  |  A   |   B   |   C   |               |
| 2  |  D   |   A   |   B   |   E   |   F   |
| 3  |  A   |   D   |   C   |   H   |   J   |
+----+------+-------+-------+-------+-------+

DEMO

Community
  • 1
  • 1
John Ruddell
  • 25,283
  • 6
  • 57
  • 86
  • Hey John, so you're query almost worked! But for some reason, it is only returning some of the values linked to each ID as opposed to all of them. Why would that be? – user3918629 Aug 07 '14 at 18:39
  • @user3918629 without seeing your data I couldn't tell you.. try running the inner query with the calculations for the counter.. see what it returns... make sure that the counter increments for each duplicate id and then resets to 1 for the next id.. you may need to order the results by the id first in a subquery.. just depends on whats happening – John Ruddell Aug 07 '14 at 18:43
  • it seems that the counter only goes up to three (which is in line with my question) but in some cases there are more than that. I don't see why the counter would stop at 3, but for some reason it is... – user3918629 Aug 07 '14 at 19:24
  • @user3918629 you have this set up incorrectly then because in my example the counter goes up to 5 because there are 5 duplicate records. you need to post the exact query – John Ruddell Aug 07 '14 at 19:30
  • I did not change anything from the query you sent me. However, even in the case where there are 5 duplicate records, the query only returns 3 of the 5. – user3918629 Aug 07 '14 at 19:41
  • I should mention that I ran the inner query with the calculations for the counter and it only ever went to 3 and no higher. – user3918629 Aug 07 '14 at 19:49
  • @user3918629 do you have an id in the inner query? i can't help without seeing what that query is... it cant be the same because the table name and columns would be different – John Ruddell Aug 07 '14 at 19:49
  • SELECT ID1, ID2, IF(@A = ID1, @B := @B + 1, @B := 1) AS counter, @A := ID1 FROM Table1 CROSS JOIN (SELECT @A := 0, @B := 0) t – user3918629 Aug 07 '14 at 19:58
  • Ok do you know what that is doing? if there are more than three id's in a row that are duplicates then it'll pull them out and the counter will go higher than 3.. try doing something like SELECT ID1, ID2 FROM Table1 ORDER BY ID1 then do a select from that where you set the counter – John Ruddell Aug 07 '14 at 20:18
  • Did the job! Thank you SO much for your help :) – user3918629 Aug 07 '14 at 20:57
  • @user3918629 sure thing! so basically it wasn't ordering the ID so it was resetting the count.. so yea thats the only thing I could think would cause the issue. glad it works! good luck – John Ruddell Aug 07 '14 at 21:13
4

The simple answer is yes there is..

The basic query you are looking for is:

  SELECT column1, GROUP_CONCAT(column2 SEPARATOR ' ')
    FROM table_name
GROUP BY column1

You can even order the results:

  SELECT column1, GROUP_CONCAT(column2 ORDER BY column2 SEPARATOR ' ')
    FROM table_name
GROUP BY column1
ORDER BY column1
Arth
  • 12,789
  • 5
  • 37
  • 69
  • It's not ordered in OPs sample result, but without a suitable column the result will be unpredictable but by your second solution. – VMai Aug 07 '14 at 13:53
  • Sorry this is my first time posting so I'm not comfortable with the formatting! I need the results to be displayed in individual columns as well. So for the first row, entries 'A', 'B' and 'C' would each be in its own column. – user3918629 Aug 07 '14 at 13:56
  • @VMai, The second solution orders the concatenation by the value of column2 in the db.. or is that what you mean? – Arth Aug 07 '14 at 13:56
  • @user3918629 That is not easily done - especially without a suitable column that guarantees a certain order of the rows. Please consider the rows of a table as an unordered set. – VMai Aug 07 '14 at 13:59
  • @user3918629 From the **Related** section on the right sidebar: http://stackoverflow.com/questions/9685601/transpose-mysql-query-rows-into-columns?rq=1 Please consider to mention such requirements in the question. You could have given every column a header. That would helped your helpers enormously. – VMai Aug 07 '14 at 14:00
  • @user3918629 Aaah, ok, then that is a lot more tricky.. it is doable but you'd be better off just querying your results and processing them in code. You'd also need to change the query every time your groups maximum size increases to add a column. Unfortunately the PIVOT operation is not supported in MySql :( – Arth Aug 07 '14 at 14:01
  • @Arth Yes, that's the only reliable ordering one can do with this set of rows. But OP has completely other needs. – VMai Aug 07 '14 at 14:02
  • @user3918629 You could run the GROUP_CONCAT() query and explode (PHP function) the resulting value around the delimiter.. but that is pretty filthy. – Arth Aug 07 '14 at 14:03
  • @Vmai I'm not sure what you mean by 'guarantees a certain order of the rows'. Can you please elaborate? – user3918629 Aug 07 '14 at 14:03
  • @VMai there is nothing to suggest the OP has any ordering requirements I was just demonstrating the GROUP_CONCAT possibilities. – Arth Aug 07 '14 at 14:04
  • @user3918629 In your second column did you add the first occurence of an element from col2 as in your table. If your table only consists of this two columns there is no such thing as the first occurence. I.e. it could be any one of A, B or C. Not simply A as in your sample. – VMai Aug 07 '14 at 14:06
  • @Arth What if I had a predefined number of columns? Would that help – user3918629 Aug 07 '14 at 14:08
  • @Arth I wasn't criticizing, only elaborating on that what you've already done. I think you gave a good answer for the question as it was then. – VMai Aug 07 '14 at 14:08
  • @Vmai Sorry about the confusion in my original post! I will keep your advice in mind for my next one :) – user3918629 Aug 07 '14 at 14:09
  • @VMai I wasn't grumbling, just pointing out what I'd done. If there is an auto_increment or time column, that could be used in the GROUP_CONCAT as I'm sure you know! :) – Arth Aug 07 '14 at 14:09
  • @user3918629 Yes, but it would still be hard.. it would be easier if you wanted a column for just the A values, another for just the B values... etc. – Arth Aug 07 '14 at 14:10
  • @JohnRuddell Good work! I knew it was possible but I'm not great with the counter variables. – Arth Aug 07 '14 at 14:30
3

Or, very similar to (and slightly slower than) JR's solution...

  DROP TABLE IF EXISTS my_table;

  CREATE TABLE my_table
  (Column1 INT NOT NULL
  ,Column2 CHAR(1) NOT NULL
  ,PRIMARY KEY(Column1,Column2)
  );

  INSERT INTO my_table VALUES
  (1       ,'A'),
  (1       ,'B'),
  (1       ,'C'),
  (2       ,'D'),
  (2       ,'A'),
  (2       ,'B'),
  (2       ,'E'),
  (2       ,'F'),
  (3       ,'A'),
  (3       ,'D'),
  (3       ,'C'),
  (3       ,'H'),
  (3       ,'J');

  SELECT column1
       , MAX(CASE WHEN rank = 1 THEN column2 END) n1
       , MAX(CASE WHEN rank = 2 THEN column2 END) n2
       , MAX(CASE WHEN rank = 3 THEN column2 END) n3
       , MAX(CASE WHEN rank = 4 THEN column2 END) n4
       , MAX(CASE WHEN rank = 5 THEN column2 END) n5
    FROM
       ( SELECT x.*
              , COUNT(*) rank 
           FROM my_table x 
           JOIN my_table y 
             ON y.column1 = x.column1 
            AND y.column2 <= x.column2 
          GROUP 
             BY x.column1
              , x.column2
       ) n
   GROUP 
      BY column1;

  +---------+------+------+------+------+------+
  | column1 | n1   | n2   | n3   | n4   | n5   |
  +---------+------+------+------+------+------+
  |       1 | A    | B    | C    | NULL | NULL |
  |       2 | A    | B    | D    | E    | F    |
  |       3 | A    | C    | D    | H    | J    |
  +---------+------+------+------+------+------+
Strawberry
  • 33,750
  • 13
  • 40
  • 57
  • you can add an `ELSE ' '` in your case to mimic the OP's desired result :) but this is a good solution for sorted results by id – John Ruddell Aug 07 '14 at 14:48