2013-02-09 101 views
1

我有一个表:MySQL的透视或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,但无法理解它在我的情况,因为我根据每条记录的总比赛建立动态列...

任何帮助将不胜感激。请让我知道,如果这没有意义。

Nino

回答

2

如果您知道最大数为12,则可以使用MAXCASE获取每个附件的行号。

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重置。应该现在工作。另外,per @ spencer7593的好评,我已经稍微更新了这个查询。

- 编辑 -

针对OP的大约需要动态结果的评论,这应该工作:

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”时,SELECT列表中的第一个a.issue_id是多余的;可以在设置@previous:= a.issue_id的行上分配列别名'issue_id'。另外,在内联视图中的ORDER BY会使语句更具确定性,因为附件将以指定的顺序返回,而不是允许MySQL以任意顺序返回它们。 – spencer7593 2013-02-09 01:55:36

+0

@ spencer7593 - 精彩评论,非常感谢!我从以前的版本错误地留下了curRow :)我编辑了我的回复 - 再次感谢! – sgeddes 2013-02-09 02:05:23

+0

@sgeddes - 谢谢!这很棒,满足了我的迫切需求。接下来,我将不得不弄清楚如何通过使用按ID分组的附件的最大数量来获得相同的结果(这就是我如何从12开始)。因此,当一个id的最大潜在附件更改时,列也会动态更改。如果你知道如何做到这一点,它将完全解决我的使用案例! – 2013-02-15 00:56:53

2

静态定义SELECT语句返回的一组列。 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子句的用途是指定只返回1行。 LIMIT 0,1指定要返回1行,从第一行开始(0)。 LIMIT 1,1只返回第二行。


这不是唯一的方法,可能不是最有效的。它可以合理地处理从外部查询返回的少量行(在你的情况下,从Issues表中获得)。为这个语句生成的“嵌套循环”计划对于大集合可能是资源密集型的(即,速度很慢)。

为获得最佳性能,您可能需要索引...

ON `Attachments` (issue_id, id) 

,或者至少在

ON `Attachments` (issue_id) 

如果你真的需要一个“动态”号返回时,你会得到更好的服务返回attachment值作为单独的行和处理从客户端的SQL语句返回的结果集。