1. 程式人生 > >[MySQL] 行列轉換變化各種方法實現總結(行變列報表統計、列變行資料記錄統計等

[MySQL] 行列轉換變化各種方法實現總結(行變列報表統計、列變行資料記錄統計等

前言:

mysql行列變化,最難的就是將多個列變成多行,使用的比較多的是統計學中行變列,列變行,沒有找到現成的函式或者語句,所以自己寫了儲存過程,使用動態sql來實現,應用業務場景,使用者每個月都有使用記錄數錄入一張表,一個月一個欄位,所以表的欄位是動態增長的,現在需要實時統計當前使用者使用的總數量,如果你知道有多少個欄位,那麼可以用select c1+c2+c3+…. From tbname where tid=’111’;來實現,但是關鍵是這個都是動態的,所以在應用程式端來實現確實不適宜,可以放在資料庫後臺在儲存過程裡實現。

而且在行變成列中,如果要寫單個sql來實現,列的數目就需要寫死,因為如果不知道要展示成多少列的話,就需要用動態變數,而一條

sql裡面無法使用動態變數。但是可以使用sql塊來實現動態的效果。
  

一,列變成行例子演示

1,準備測試資料

這是基礎資料表,裡面有多個欄位wm201403……,現在需要把N個這樣的列變成行資料。

  1. USE csdn;  
  2. DROPTABLE IF EXISTS flow_table;  
  3. CREATETABLE `flow_table` (  
  4.   `ID` INT(11) NOTNULL AUTO_INCREMENT,  
  5.   `Number` BIGINT(11) NOTNULL,  
  6.   `City` VARCHAR(10) NOTNULL,  
  7.   `wm201403` DECIMAL
    (7,2) DEFAULTNULL,  
  8.   `wm201404` DECIMAL(7,2) DEFAULTNULL,  
  9.   `wm201405` DECIMAL(7,2) DEFAULTNULL,  
  10.   `wm201406` DECIMAL(7,2) DEFAULTNULL,  
  11.   `wm201407` DECIMAL(7,2) DEFAULTNULL,  
  12.   `wm201408` DECIMAL(7,2) DEFAULTNULL,  
  13.   PRIMARYKEY (`ID`,`Number`)  
  14. ) ENGINE=INNODB   DEFAULT CHARSET=utf8;  

錄入一批測試資料:

  1. INSERTINTO flow_table(Number,City,wm201403,wm201404,wm201405,wm201406,wm201407,wm201408)SELECT 1,'shanghai',100.2,180.4,141,164,124,127;  
  2. INSERTINTO flow_table(Number,City,wm201403,wm201404,wm201405,wm201406,wm201407,wm201408)SELECT 2,'shanghai',110.23,180.34,141.23,104.78,124.67,127.45;  
  3. INSERTINTO flow_table(Number,City,wm201403,wm201404,wm201405,wm201406,wm201407,wm201408)SELECT 3,'beijing',123.23,110.34,131.33,154.58,154.67,167.45;  
  4. INSERTINTO flow_table(Number,City,wm201403,wm201404,wm201405,wm201406,wm201407,wm201408)SELECT 4,'hangzhou',0,110.34,131.33,154.58,154.67,0;  
  5. INSERTINTO flow_table(Number,City,wm201405,wm201406,wm201407,wm201408)SELECT 5,'hangzhou',131.33,154.58,154.67,0;  

需要達到的統計效果是:

+--------+-----------+

| Number | total_num |

+--------+-----------+

|      1 |    836.60 |

|      2 |    788.70 |

|      3 |    841.60 |

|      4 |    550.92 |

|      5 |    440.58 |

+--------+-----------+

5 rows in set (0.00 sec)

2,儲存過程遍歷:

這個儲存過程建立了2張臨時表,查詢測試表資料形成遊標,遍歷遊標根據主鍵Number來呼叫pro_flow_modify儲存過程進行行列變化。程式碼如下:

  1. DELIMITER $$  
  2. DROPPROCEDURE IF EXISTS csdn.`proc_all_changes`$$  
  3. CREATEPROCEDURE csdn.proc_all_changes()  
  4. BEGIN
  5.     DECLARE v_number BIGINT;  
  6.     DECLARE v_city VARCHAR(10);  
  7.     DECLARE _done INTDEFAULT 0;   
  8.     /*定義遊標*/  
  9.     DECLARE cur_all CURSORFORSELECT Number,City FROM csdn.`flow_table`;  
  10.     /**這裡如果需要定義下當NOT FOUND的時候,EXIT退出遊標遍歷,不然如果設定成CONTINUE會一直執行下去。*/  
  11.     DECLARE EXIT HANDLER FORNOT FOUND BEGINSET _done=1;END;        
  12.        /*建立臨時表,存放所有欄位的臨時表*/  
  13.     DROPTABLE IF EXISTS flow_n_columns;  
  14.     CREATETABLE `flow_n_columns` (  
  15.       `column_name` VARCHAR(10) NOTNULL
  16.     ) ENGINE=INNODB DEFAULT CHARSET=utf8;  
  17.     /*存放最終變成行的資料表*/  
  18.     DROPTABLE IF EXISTS flow_tmp;  
  19.     CREATETABLE `flow_tmp` (  
  20.       `Number` INT(11) DEFAULTNULL,  
  21.       `City` VARCHAR(10) DEFAULTNULL,  
  22.       `wm_str` VARCHAR(10) DEFAULTNULL,  
  23.       `Wm` DECIMAL(7,2) DEFAULTNULL
  24.     ) ENGINE=INNODB DEFAULT CHARSET=utf8;  
  25.     OPEN cur_all;  
  26.     REPEAT  
  27.         FETCH cur_all INTO v_number, v_city;  
  28.         IF NOT _done THEN
  29.         CALL csdn.pro_flow_modify(v_number,v_city);  
  30.         END IF;   
  31.         UNTIL _done=1 END REPEAT;  
  32.     CLOSE cur_all;   
  33.         /*展示下所有的行轉列的資料**/  
  34.     SELECT * FROM csdn.flow_tmp;        
  35. END$$     
  36. DELIMITER ;  


3,行裡變化儲存過程

通過查詢系統表information_schema.`COLUMNS`來獲取測試表flow_table的所有列,然後寫動態SQL,來把列的值錄入到臨時表flow_tmp中。

  1. DELIMITER $$  
  2. DROPPROCEDURE IF EXISTS csdn.`pro_flow_modify`$$  
  3. CREATEPROCEDURE csdn.`pro_flow_modify`(p_Number INT,p_city VARCHAR(10))  
  4. BEGIN
  5.     DECLARE v_column_name VARCHAR(10) DEFAULT'';  
  6.     DECLARE v_exe_sql VARCHAR(1000) DEFAULT'';  
  7.     DECLARE v_start_wm VARCHAR(10) DEFAULT'';  
  8.     DECLARE v_end_wm VARCHAR(10) DEFAULT'';  
  9.     DECLARE v_num  DECIMAL(10,2) DEFAULT 0;  
  10.     DECLARE i INTDEFAULT 1;  
  11.     DECLARE v_Number INTDEFAULT 0;  
  12.     SET v_Number=p_Number;  
  13.     DELETEFROM csdn.flow_n_columns;  
  14.     DELETEFROM csdn.flow_tmp WHERE Number=v_Number;  
  15.     /*把測試表flow_table的所有欄位都錄入欄位臨時表中,這樣就達到了從列變成行的目的*/  
  16.     INSERTINTO flow_n_columns  
  17.     SELECT t.`COLUMN_NAME` FROM information_schema.`COLUMNS` t WHERE t.`TABLE_NAME`='flow_table'AND t.`TABLE_SCHEMA`='csdn'AND t.`COLUMN_NAME` NOTIN('ID','Number','City');  
  18.     SELECT column_name INTO v_column_name FROM csdn.flow_n_columns LIMIT 1;