2013-02-09 100 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語句返回的結果集。