Запрос MySQL GROUP_CONCAT по нескольким строкам

Я получаю данные имени и адреса из общих данных вопроса/ответа, чтобы создать какую-то нормализованную базу данных отчетов.

Запрос, который у меня есть, использует group_concat и работает для отдельных наборов вопросов, но не для нескольких наборов.

Я попытался упростить то, что я делаю, используя только имя и фамилию и всего 3 записи, 2 для одного человека и 1 для другого. В действительности же существует более 300 000 записей.

Пример результатов с qs.Id = 1.

QuestionSetId       Forename        Surname
-------------------------------------------------------
            1       Bob             Jones

Пример результатов с qs.Id IN (1, 2, 3).

QuestionSetId       Forename        Surname
-------------------------------------------------------
            3       Bob,Bob,Frank   Jones,Jones,Smith

Что я хотел бы видеть для qs.Id IN (1, 2, 3).

QuestionSetId       Forename        Surname
-------------------------------------------------------
            1       Bob             Jones
            2       Bob             Jones
            3       Frank           Smith

Итак, как я могу заставить второй пример возвращать отдельную строку для каждого набора информации об имени и адресе?

Я понимаю, что текущий способ хранения данных "сомнительный", но я не могу изменить способ хранения данных.

Я могу получить наборы отдельных ответов, но не знаю, как объединить остальные.

Моя упрощенная схема, которую я не могу изменить:

CREATE TABLE StaticQuestion (
  Id INT NOT NULL,
  StaticText VARCHAR(500) NOT NULL);

CREATE TABLE Question (
  Id INT NOT NULL, 
  Text VARCHAR(500) NOT NULL);

CREATE TABLE StaticQuestionQuestionLink (
  Id INT NOT NULL,
  StaticQuestionId INT NOT NULL,
  QuestionId INT NOT NULL,
  DateEffective DATETIME NOT NULL);

CREATE TABLE Answer (
  Id INT NOT NULL,
  Text VARCHAR(500) NOT NULL);

CREATE TABLE QuestionSet (
  Id INT NOT NULL,
  DateEffective DATETIME NOT NULL);

CREATE TABLE QuestionAnswerLink (
  Id INT NOT NULL,
  QuestionSetId INT NOT NULL,
  QuestionId INT NOT NULL,
  AnswerId INT NOT NULL,
  StaticQuestionId INT NOT NULL);

Некоторые примеры данных только для имени и фамилии.

INSERT INTO StaticQuestion (Id, StaticText)
VALUES (1, 'FirstName'),
       (2, 'LastName');

INSERT INTO Question (Id, Text)
VALUES (1, 'What is your first name?'),
       (2, 'What is your forename?'),
       (3, 'What is your Surname?');

INSERT INTO StaticQuestionQuestionLink (Id, StaticQuestionId, QuestionId, DateEffective)
VALUES (1, 1, 1, '2001-01-01'),
       (2, 1, 2, '2008-08-08'),
       (3, 2, 3, '2001-01-01');

INSERT INTO Answer (Id, Text)
VALUES (1, 'Bob'),
       (2, 'Jones'),
       (3, 'Bob'),
       (4, 'Jones'),
       (5, 'Frank'),
       (6, 'Smith');

INSERT INTO QuestionSet (Id, DateEffective)
VALUES (1, '2002-03-25'),
       (2, '2009-05-05'),
       (3, '2009-08-06');

INSERT INTO QuestionAnswerLink (Id, QuestionSetId, QuestionId, AnswerId, StaticQuestionId)
VALUES (1, 1, 1, 1, 1),
       (2, 1, 3, 2, 2),
       (3, 2, 2, 3, 1),
       (4, 2, 3, 4, 2),
       (5, 3, 2, 5, 1),
       (6, 3, 3, 6, 2);

На всякий случай, если SQLFiddle не работает, вот 3 запроса из примеров, на которые я ссылался:

1: - рабочий запрос, но только на 1 наборе данных.

SELECT MAX(QuestionSetId) AS QuestionSetId,
       GROUP_CONCAT(Forename) AS Forename,
       GROUP_CONCAT(Surname) AS Surname
FROM   (SELECT
            x.QuestionSetId,
            CASE x.StaticQuestionId WHEN 1 THEN Text END AS Forename,
            CASE x.StaticQuestionId WHEN 2 THEN Text END AS Surname        
        FROM     (SELECT (SELECT   link.StaticQuestionId
                          FROM     StaticQuestionQuestionLink link
                          WHERE    link.Id = qa.QuestionId
                               AND link.DateEffective <= qs.DateEffective
                               AND link.StaticQuestionId IN (1, 2)
                          ORDER BY link.DateEffective DESC LIMIT 1) AS StaticQuestionId,
                         a.Text,
                         qa.QuestionSetId
                  FROM   QuestionSet qs
                            INNER JOIN QuestionAnswerLink qa ON qs.Id = qa.QuestionSetId
                            INNER JOIN Answer a ON qa.AnswerId = a.Id
                  WHERE  qs.Id IN (1)) x) y

2: - рабочий запрос, но нежелательные результаты для нескольких наборов данных.

SELECT MAX(QuestionSetId) AS QuestionSetId,
       GROUP_CONCAT(Forename) AS Forename,
       GROUP_CONCAT(Surname) AS Surname
FROM   (SELECT
            x.QuestionSetId,
            CASE x.StaticQuestionId WHEN 1 THEN Text END AS Forename,
            CASE x.StaticQuestionId WHEN 2 THEN Text END AS Surname        
        FROM     (SELECT (SELECT   link.StaticQuestionId
                          FROM     StaticQuestionQuestionLink link
                          WHERE    link.Id = qa.QuestionId
                               AND link.DateEffective <= qs.DateEffective
                               AND link.StaticQuestionId IN (1, 2)
                          ORDER BY link.DateEffective DESC LIMIT 1) AS StaticQuestionId,
                         a.Text,
                         qa.QuestionSetId
                  FROM   QuestionSet qs
                            INNER JOIN QuestionAnswerLink qa ON qs.Id = qa.QuestionSetId
                            INNER JOIN Answer a ON qa.AnswerId = a.Id
                  WHERE  qs.Id IN (1, 2, 3)) x) y

3: - рабочий запрос для нескольких наборов данных только в 1 поле (ответ).

SELECT
    qs.Id AS QuestionSet,
    a.Text AS Answer
FROM
    QuestionSet qs
        INNER JOIN QuestionAnswerLink qalink ON qs.Id = qalink.QuestionSetId
        INNER JOIN StaticQuestionQuestionLink sqqlink ON qalink.QuestionId = sqqlink.QuestionId
        INNER JOIN Answer a ON qalink.AnswerId = a.Id
WHERE
        sqqlink.StaticQuestionId = 1 /* FirstName */
    AND sqqlink.DateEffective = 
            (SELECT DateEffective
               FROM StaticQuestionQuestionLink
              WHERE StaticQuestionId = 1 
                AND DateEffective <= qs.DateEffective
           ORDER BY DateEffective
               DESC 
              LIMIT 1)

person PeteGO    schedule 06.12.2012    source источник
comment
+1 Красиво написанный вопрос, единственное, что я бы порекомендовал, это разместить ваши запросы из скрипта SQL на этом сайте. В случае, если скрипка sql не работает, мы не можем получить доступ к тем запросам, которые вы уже написали. :)   -  person Taryn    schedule 07.12.2012
comment
+1 за действительно сложный вопрос   -  person Olaf Dietsche    schedule 07.12.2012


Ответы (2)


Воруя как у @PixelMaker, так и у @PeteGO, я добавлю это

SELECT qs.Id AS QuestionSetId,
       GROUP_CONCAT(a.Text order by a.id) AS Answer
FROM QuestionSet qs
JOIN QuestionAnswerLink qa ON qs.Id = qa.QuestionSetId
JOIN StaticQuestionQuestionLink link ON qa.QuestionId = link.QuestionId
JOIN Answer a ON qa.AnswerId = a.Id
WHERE link.Id = qa.QuestionId
      AND link.DateEffective <= qs.DateEffective
      AND link.StaticQuestionId IN (1, 2)
      and qs.id in (1,2,3)
GROUP BY qs.Id

скрипт SQL

и, наконец, этот скрипт SQL

SELECT qs.Id AS QuestionSetId,
       GROUP_CONCAT(case link.staticquestionid when 1 then a.Text end) AS forename,
       GROUP_CONCAT(case link.staticquestionid when 2 then a.Text end) AS surname
FROM QuestionSet qs
JOIN QuestionAnswerLink qa ON qs.Id = qa.QuestionSetId
JOIN StaticQuestionQuestionLink link ON qa.QuestionId = link.QuestionId
JOIN Answer a ON qa.AnswerId = a.Id
WHERE link.Id = qa.QuestionId
      AND link.DateEffective <= qs.DateEffective
      AND link.StaticQuestionId IN (1, 2)
      and qs.id in (1,2,3)
GROUP BY qs.Id

что дает желаемый результат.

person Olaf Dietsche    schedule 06.12.2012
comment
Идеально - я считаю! Я попробую это на основном решении, спасибо. - person PeteGO; 07.12.2012

@PeteGo, попробуй это

SELECT
    qs.Id AS QuestionSet,
    GROUP_CONCAT(a.Text SEPARATOR ', ')  AS Answer
FROM
    QuestionSet qs
        INNER JOIN QuestionAnswerLink qalink ON qs.Id = qalink.QuestionSetId
        INNER JOIN StaticQuestionQuestionLink sqqlink ON qalink.QuestionId = sqqlink.QuestionId
        INNER JOIN Answer a ON qalink.AnswerId = a.Id
WHERE
        sqqlink.StaticQuestionId in (1,2,3) /* FirstName */
GROUP BY qs.Id;

OR

SELECT
    qs.Id AS QuestionSet,
    group_CONCAT(b.Text ORDER BY b.Id )   AS Answer
FROM
    QuestionSet qs
        INNER JOIN QuestionAnswerLink qalink ON qs.Id = qalink.QuestionSetId
        INNER JOIN StaticQuestionQuestionLink sqqlink ON qalink.QuestionId = sqqlink.QuestionId
        INNER JOIN Answer a ON qalink.AnswerId = a.Id
INNER JOIN Answer b ON qalink.AnswerId = b.Id
WHERE
        sqqlink.StaticQuestionId in (1,2,3)  
   group by qs.Id  ;

OR

select qs.Id, group_concat(a.Text order by a.Id) from QuestionAnswerLink qalink
left join QuestionSet qs on qalink.QuestionSetId=qs.Id
left join Answer a on qalink.AnswerId = a.Id


 left join QuestionSet qs1 on qalink.QuestionSetId=qs1.Id
left join Answer b on qalink.AnswerId = b.Id


group by qs.Id ;
person Pixel Maker    schedule 06.12.2012
comment
Спасибо за быстрый ответ, но я действительно хочу, чтобы каждый ответ был в отдельной колонке. - person PeteGO; 07.12.2012