1

我有一张桌子:

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 透视行转换为动态列数,但在我的情况下无法理解它,因为我正在根据每条记录的总匹配数构建动态列......

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

尼诺

4

2 回答 2

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

我必须进行编辑以使每个组的行号重置。现在应该可以上班了。另外,根据@spencer7593 的精彩评论,我稍微更新了查询。

- 编辑 -

为了回应OP关于需要动态结果的评论,这应该有效:

设置@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

于 2013-02-09T00:33:13.870 回答
2

SELECT 语句返回的列集是静态定义的。SELECT 语句不能返回“可变”数量的列。

如果您可以定义要返回的列,则可以生成您显示的结果集,在您的情况下,这意味着定义attachment将在一行上返回的最大值(列)数。

获取该结果集的一种方法是使用 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 语句返回的结果集。

于 2013-02-09T00:27:23.327 回答