2013-02-09 5 views
1

У меня есть таблица:MySQL Pivot или Excel Решение

CREATE TABLE `Issues` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `title` varchar(255) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB; 

У меня есть еще один стол:

CREATE TABLE `Attachments` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `issue_id` int(11) DEFAULT NULL, 
    `attachment` text, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB; 

Как я могу получить данные, чтобы выглядеть следующим образом:

issue_id title attachment1 attachment2 attachment3 
-------------------------------------------------------------- 
1   T1  a1.png   a2.png 
2   T2 
3   T3  b4.gif   xyz.doc  ttt.file 

Проблема, которую я не могу понять, заключается в том, как получить динамический набор вложений в динамический столбец, сгруппированный по проблеме. Я определил, что максимальное количество вложений для одной проблемы составляет 12, но общая сумма за билет может быть от 0 до 12. Я в тупике ...

Я попробовал эту MySQL pivot row into dynamic number of columns, но не могу понять это в моем случае, потому что я построение динамических столбцов на основе всех матчей на запись ...

Любая помощь будет очень признательна. Пожалуйста, дайте мне знать, если это не имеет смысла.

Нино

ответ

2

Вот один из способов добиться этого, если вы знаете, что максимальное число равно 12. Оно использует MAX и CASE, получая номер строки для каждого приложения.

SELECT 
    I.Id issue_id, 
    I.title, 
    MAX(CASE WHEN d.row_number = 1 THEN D.attachment END) attachment1, 
    MAX(CASE WHEN d.row_number = 2 THEN D.attachment END) attachment2, 
    MAX(CASE WHEN d.row_number = 3 THEN D.attachment END) attachment3, 
    MAX(CASE WHEN d.row_number = 4 THEN D.attachment END) attachment4, 
    MAX(CASE WHEN d.row_number = 5 THEN D.attachment END) attachment5, 
    MAX(CASE WHEN d.row_number = 6 THEN D.attachment END) attachment6, 
    MAX(CASE WHEN d.row_number = 7 THEN D.attachment END) attachment7, 
    MAX(CASE WHEN d.row_number = 8 THEN D.attachment END) attachment8, 
    MAX(CASE WHEN d.row_number = 9 THEN D.attachment END) attachment9, 
    MAX(CASE WHEN d.row_number = 10 THEN D.attachment END) attachment10, 
    MAX(CASE WHEN d.row_number = 11 THEN D.attachment END) attachment11, 
    MAX(CASE WHEN d.row_number = 12 THEN D.attachment END) attachment12 
FROM Issues I 
    LEFT JOIN (
    SELECT 
     a.issue_id, 
     @running:=if(@previous=a.issue_id,@running,0) + 1 as row_number, 
     @previous:=a.issue_id, 
     a.attachment 
    FROM Attachments a 
     JOIN (SELECT @previous := 0) r 
    ORDER BY a.issue_id, a.attachment 
) D ON I.ID = D.issue_id 
GROUP BY I.Id, I.Title 

И вот SQL Fiddle.

Мне нужно было сделать редактирование, чтобы сделать сброс rownumber для каждой группы. Должно быть сейчас. Кроме того, в замечательном замечании @ spencer7593 я немного обновил запрос.

--EDIT--

В ответ на комментарий ФП в о необходимости динамических результатов, это должно работать:

SET @sql = NULL;

SELECT 
    GROUP_CONCAT(DISTINCT 
    CONCAT(
     'MAX(IF(d.row_number = ', d.row_number, ',D.attachment,NULL)) AS attachment', d.row_number) 
) INTO @sql 
FROM Issues I 
    LEFT JOIN (
    SELECT 
     a.issue_id, 
     @running:=if(@previous=a.issue_id,@running,0) + 1 as row_number, 
     @previous:=a.issue_id, 
     a.attachment 
    FROM Attachments a 
     JOIN (SELECT @previous := 0) r 
    ORDER BY a.issue_id, a.attachment 
) D ON I.ID = D.issue_id 
; 

SET @sql = CONCAT('SELECT I.Id issue_id, 
          I.title, ', @sql, ' 
        FROM Issues I 
        LEFT JOIN (
        SELECT 
         a.issue_id, 
         @running:=if(@previous=a.issue_id,@running,0) + 1 as row_number, 
         @previous:=a.issue_id, 
         a.attachment 
        FROM Attachments a 
         JOIN (SELECT @previous := 0) r 
        ORDER BY a.issue_id, a.attachment 
       ) D ON I.ID = D.issue_id 
       GROUP BY I.Id, I.Title'); 

PREPARE stmt FROM @sql; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 

И вот SQL Fiddle.

+1

+1. Было бы неплохо инициализировать '@previous: = NULL' в встроенном представлении с псевдонимом как' r', чтобы изолировать оператор от любого предыдущего значения, хранящегося в переменной @previous. В встроенном представлении с псевдонимом «d» первый a.issue_id в списке SELECT избыточен; псевдоним столбца 'issue_id' может быть назначен в строке, которая устанавливает @previous: = a.issue_id. Кроме того, ORDER BY в этом встроенном представлении сделает оператор более детерминированным, поскольку вложения будут возвращаться в указанной последовательности, а не позволять MySQL возвращать их в произвольном порядке. – spencer7593

+0

@ spencer7593 - замечательные комментарии, большое спасибо! Я забыл curRow по ошибке из предыдущей версии :) Я отредактировал свой ответ - еще раз спасибо! – sgeddes

+0

@sgeddes - Спасибо! Это здорово и отвечает моей непосредственной необходимости. Затем мне нужно будет выяснить, как получить тот же результат, используя максимальное количество вложений, сгруппированных по id (вот как я получил 12, чтобы начать). Поэтому, когда изменяются максимальные потенциальные вложения для одного идентификатора, столбцы также будут динамически изменяться. Если вы знаете, как это сделать, это полностью решит мой случай использования! –

2

Набор столбцов, возвращаемых ЗЕЬЕСТ статически определены. Оператор SELECT не может возвращать «переменное» количество столбцов.

Возможно создание результатов, которые вы показываете, если вы можете определить возвращаемые столбцы, что в вашем случае означает определение максимального количества значений (столбцов) , которые будут возвращены в строке.

Один из подходов к получению этого набора результатов - использовать коррелированный подзапрос в списке SELECT для возврата первого, второго, третьего и т. Д. Значений attachment.

SELECT i.id 
    , i.title 
    , (SELECT a1.attachment 
      FROM `Attachments` a1 
      WHERE a1.issue_id = i.id 
      ORDER BY a1.id 
      LIMIT 0,1 
     ) AS attachment1 
    , (SELECT a2.attachment 
      FROM `Attachments` a2 
      WHERE a2.issue_id = i.id 
      ORDER BY a2.id 
      LIMIT 1,1 
     ) AS attachment2 
    , (SELECT a3.attachment 
      FROM `Attachments` a3 
      WHERE a3.issue_id = i.id 
      ORDER BY a3.id 
      LIMIT 2,1 
     ) AS attachment3 
    FROM `Issues` i 
ORDER BY i.id 

Чтобы вернуть максимальное количество привязанности, вам нужно будет продлить, что ...

 , (SELECT a4.attachment 
      FROM `Attachments` a4 
      WHERE a4.issue_id = i.id 
      ORDER BY a4.id 
      LIMIT 3,1 
     ) AS attachment4 

Цель ORDER BY, чтобы сделать результирующий из запроса детерминированным (при отсутствии ORDER BY MySQL может возвращать строки в любом порядке, который он хочет.)

Цель предложения LIMIT заключается в том, чтобы указать, что возвращается только одна строка. LIMIT 0,1 указывает, что 1 строка должна быть возвращена, начиная с первой строки (0). LIMIT 1,1 возвращает только вторую строку.


Это не единственный подход и, возможно, не самый эффективный. Он может работать разумно для небольшого количества строк, возвращаемых из внешнего запроса (в вашем случае из таблицы Issues. План «вложенных циклов», созданный для этого оператора, может быть ресурсоемким (т.е. медленным) для большого набора.

Для лучшей производительности вам, скорее всего, понадобится индекс ...

ON `Attachments` (issue_id, id) 

или, по крайней мере, на

ON `Attachments` (issue_id) 

Если вы действительно нуждаетесь в «динамический» номер вернулся, вы будете лучше обслужены возвращения attachment значения в виде отдельных строк и обработка результат возвращается из инструкции SQL на стороне клиента.