简体   繁体   中英

What is the best way to query this?

I have tables that look like this:

questions:
id description
1   Q1
2   Q2

answers:
id question_id x y description
1     1        1 2   A1
2     1        3 4   A2
3     2        5 6   A3
4     2        7 8   A4

What I want to get is a query that can output this:

Q1 A1 1,2 A2 3,4
Q2 A3 5,6 A4 7,8

I've been pulling my hair for days now trying to figure this out. I'm doing this in PHP and MySQL so if anyone can shed some light out there, that'd be really great.

EDIT: I forgot to mention that I'm using CodeIgniter for this, too. So, that might help with the answers.

Considering that there might be a random number of answers per question, you cannot design a query that will return a fixed number of columns. You have to return a single result per question and then do a little bit of parsing in your code.

The GROUP_CONCAT function can be helpful for this kind of problem:

SELECT q.description, GROUP_CONCAT(
    CONCAT(a.description,' ',a.x,',',a.y) ORDER BY a.id
    SEPARATOR ' '
    ) AS answers
FROM questions q
  JOIN answers a ON a.question_id = q.id
GROUP BY q.description;

Will return

+-------------+---------------+
| description | answers       |
+-------------+---------------+
| Q1          | A1 1,2 A2 3,4 |
| Q2          | A3 5,6 A4 7,8 |
+-------------+---------------+
2 rows in set (0.00 sec)

You can change the SEPARATOR value by whatever you want to parse the result in your code. You can use the ORDER BY clause of the GROUP_CONCAT function to order the answers in the returned result for each answer (here I ordered by answer id).

Edit : If you are sure that there will never be more than 4 answers per question, you can issue the following query to put each answer in its own column:

SELECT description,
  REPLACE(SUBSTRING(SUBSTRING_INDEX(answers, '$', 1), LENGTH(SUBSTRING_INDEX(answers, '$', 1 - 1)) + 1), '$', '') answer_1,
  REPLACE(SUBSTRING(SUBSTRING_INDEX(answers, '$', 2), LENGTH(SUBSTRING_INDEX(answers, '$', 2 - 1)) + 1), '$', '') answer_2,
  REPLACE(SUBSTRING(SUBSTRING_INDEX(answers, '$', 3), LENGTH(SUBSTRING_INDEX(answers, '$', 3 - 1)) + 1), '$', '') answer_3,
  REPLACE(SUBSTRING(SUBSTRING_INDEX(answers, '$', 4), LENGTH(SUBSTRING_INDEX(answers, '$', 4 - 1)) + 1), '$', '') answer_4
FROM (
    SELECT q.description, GROUP_CONCAT(
        CONCAT(a.description,' ',a.x,',',a.y) ORDER BY a.id
        SEPARATOR '$'
        ) AS answers
    FROM questions q
      JOIN answers a ON a.question_id = q.id
    GROUP BY q.description
  ) t;

Will return

+-------------+----------+----------+----------+----------+
| description | answer_1 | answer_2 | answer_3 | answer_4 |
+-------------+----------+----------+----------+----------+
| Q1          | A1 1,2   | A2 3,4   |          |          |
| Q2          | A3 5,6   | A4 7,8   | A5 9,10  |          |
+-------------+----------+----------+----------+----------+
2 rows in set (0.00 sec)

I added an answer to the second question for the illustration.

select * from answers ORDER BY question_id

$question_id = 0;
$print_ln = null;
foreach ($result as $row) {
  if ($question_id != $row['question_id']) { 
     echo "<br>";
     $question_id = $row['question_id'];
     $print_ln = "Q" . $row['question_id'] . " " . $row['description'] . " " . $row['x'] . "," . $row['y'];
  } else { 
     $print_ln = $print_ln . " " . $row['description'] . " " . $row['x'] . "," . $row['y'];
  }
  echo $print_ln;
 }

Note, this code still needs a bit of work... its to give you an idea on how to do it.

This query:

SELECT 
      q.id
    , q.description
    , a.description
    , CONCAT(a.x, ',', a.y)
FROM questions AS q
    JOIN answers AS a
        ON a.question_id = q.id
ORDER BY q.id
       , a.id

will show:

| 1 | Q1 | A1 | 1,2 |
| 1 | Q1 | A2 | 3,4 |
| 2 | Q2 | A3 | 5,6 |
| 2 | Q2 | A4 | 7,8 |

What you describe as final result is called pivoting and it's not easy in MySQL and depends on the data you have. For example, what should be shown if for a question, there are more than 2 answers?

And why will the result be this:

| Q1 | A1 | 1,2 | A2 | 3,4 | 
| Q2 | A3 | 5,6 | A4 | 7,8 |

and not like that?:

| Q1 | A1 | 1,2 | A2 | 3,4 | 
| Q2 | A4 | 7,8 | A3 | 5,6 | 

Anyway, for maximum of 4 answers per questions and ordering per answers.id , this will work. It may be best though to use the previous query and do the pivoting in PHP, where you can handle variable number of columns without problem:

SELECT 
      q.id
    , q.description
    , ( SELECT a.description
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 0,1
      ) AS answer1
    , ( SELECT CONCAT(a.x, ',', a.y)
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 0,1
      ) AS xy1
    , ( SELECT a.description
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 1,1
      ) AS answer2
    , ( SELECT CONCAT(a.x, ',', a.y)
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 1,1
      ) AS xy2
    , ( SELECT a.description
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 2,1
      ) AS answer3
    , ( SELECT CONCAT(a.x, ',', a.y)
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 2,1
      ) AS xy3
    , ( SELECT a.description
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 3,1
      ) AS answer4
    , ( SELECT CONCAT(a.x, ',', a.y)
        FROM answers AS a
        WHERE a.question_id = q.id
        ORDER BY a.id  LIMIT 3,1
      ) AS xy4
FROM questions AS q
ORDER BY q.id  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM