[MySQL] 行列轉換變化各種方法實現總結(行變列報表統計、列變行資料記錄統計等
前言:
mysql行列變化,最難的就是將多個列變成多行,使用的比較多的是統計學中行變列,列變行,沒有找到現成的函式或者語句,所以自己寫了儲存過程,使用動態sql來實現,應用業務場景,使用者每個月都有使用記錄數錄入一張表,一個月一個欄位,所以表的欄位是動態增長的,現在需要實時統計當前使用者使用的總數量,如果你知道有多少個欄位,那麼可以用select c1+c2+c3+…. From tbname where tid=’111’;來實現,但是關鍵是這個都是動態的,所以在應用程式端來實現確實不適宜,可以放在資料庫後臺在儲存過程裡實現。
而且在行變成列中,如果要寫單個sql來實現,列的數目就需要寫死,因為如果不知道要展示成多少列的話,就需要用動態變數,而一條
一,列變成行例子演示
1,準備測試資料
這是基礎資料表,裡面有多個欄位wm201403……,現在需要把N個這樣的列變成行資料。
- USE csdn;
- DROPTABLE IF EXISTS flow_table;
- CREATETABLE `flow_table` (
- `ID` INT(11) NOTNULL AUTO_INCREMENT,
- `Number` BIGINT(11) NOTNULL,
- `City` VARCHAR(10) NOTNULL,
-
`wm201403` DECIMAL
- `wm201404` DECIMAL(7,2) DEFAULTNULL,
- `wm201405` DECIMAL(7,2) DEFAULTNULL,
- `wm201406` DECIMAL(7,2) DEFAULTNULL,
- `wm201407` DECIMAL(7,2) DEFAULTNULL,
- `wm201408` DECIMAL(7,2) DEFAULTNULL,
- PRIMARYKEY (`ID`,`Number`)
- ) ENGINE=INNODB DEFAULT CHARSET=utf8;
錄入一批測試資料:
- INSERTINTO flow_table(Number,City,wm201403,wm201404,wm201405,wm201406,wm201407,wm201408)SELECT 1,'shanghai',100.2,180.4,141,164,124,127;
- 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;
- 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;
- INSERTINTO flow_table(Number,City,wm201403,wm201404,wm201405,wm201406,wm201407,wm201408)SELECT 4,'hangzhou',0,110.34,131.33,154.58,154.67,0;
- 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儲存過程進行行列變化。程式碼如下:
- DELIMITER $$
- DROPPROCEDURE IF EXISTS csdn.`proc_all_changes`$$
- CREATEPROCEDURE csdn.proc_all_changes()
- BEGIN
- DECLARE v_number BIGINT;
- DECLARE v_city VARCHAR(10);
- DECLARE _done INTDEFAULT 0;
- /*定義遊標*/
- DECLARE cur_all CURSORFORSELECT Number,City FROM csdn.`flow_table`;
- /**這裡如果需要定義下當NOT FOUND的時候,EXIT退出遊標遍歷,不然如果設定成CONTINUE會一直執行下去。*/
- DECLARE EXIT HANDLER FORNOT FOUND BEGINSET _done=1;END;
- /*建立臨時表,存放所有欄位的臨時表*/
- DROPTABLE IF EXISTS flow_n_columns;
- CREATETABLE `flow_n_columns` (
- `column_name` VARCHAR(10) NOTNULL
- ) ENGINE=INNODB DEFAULT CHARSET=utf8;
- /*存放最終變成行的資料表*/
- DROPTABLE IF EXISTS flow_tmp;
- CREATETABLE `flow_tmp` (
- `Number` INT(11) DEFAULTNULL,
- `City` VARCHAR(10) DEFAULTNULL,
- `wm_str` VARCHAR(10) DEFAULTNULL,
- `Wm` DECIMAL(7,2) DEFAULTNULL
- ) ENGINE=INNODB DEFAULT CHARSET=utf8;
- OPEN cur_all;
- REPEAT
- FETCH cur_all INTO v_number, v_city;
- IF NOT _done THEN
- CALL csdn.pro_flow_modify(v_number,v_city);
- END IF;
- UNTIL _done=1 END REPEAT;
- CLOSE cur_all;
- /*展示下所有的行轉列的資料**/
- SELECT * FROM csdn.flow_tmp;
- END$$
- DELIMITER ;
3,行裡變化儲存過程
通過查詢系統表information_schema.`COLUMNS`來獲取測試表flow_table的所有列,然後寫動態SQL,來把列的值錄入到臨時表flow_tmp中。
- DELIMITER $$
- DROPPROCEDURE IF EXISTS csdn.`pro_flow_modify`$$
- CREATEPROCEDURE csdn.`pro_flow_modify`(p_Number INT,p_city VARCHAR(10))
- BEGIN
- DECLARE v_column_name VARCHAR(10) DEFAULT'';
- DECLARE v_exe_sql VARCHAR(1000) DEFAULT'';
- DECLARE v_start_wm VARCHAR(10) DEFAULT'';
- DECLARE v_end_wm VARCHAR(10) DEFAULT'';
- DECLARE v_num DECIMAL(10,2) DEFAULT 0;
- DECLARE i INTDEFAULT 1;
- DECLARE v_Number INTDEFAULT 0;
- SET v_Number=p_Number;
- DELETEFROM csdn.flow_n_columns;
- DELETEFROM csdn.flow_tmp WHERE Number=v_Number;
- /*把測試表flow_table的所有欄位都錄入欄位臨時表中,這樣就達到了從列變成行的目的*/
- INSERTINTO flow_n_columns
- 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');
- SELECT column_name INTO v_column_name FROM csdn.flow_n_columns LIMIT 1;