MySQL開發技巧 第二禪(行轉列 列轉行、生成唯一的序列、刪除重複的資料)
一、如何進行行列或者列行的轉換
行轉列的關鍵是union ,列轉行的關鍵是join
需要進行行轉列的場景
報表的設計
(假設A表是一行一行的資料 分別是日期對應的銷售金額有多行。需要將其轉換為一列一列的資料 第一行是日期 第二行是銷售金額)
彙總的顯示
需求分析
將A表結構轉至成B 再將B結構轉至成C
mysql> select a.user_name as 姓名,c.kills as 打怪數 from a join c on a.id = c.user_id;
A +-----------+-----------+
| 姓名 | 打怪數 |
+-----------+-----------+
| 豬八戒 | 10 |
| 豬八戒 | 2 |
| 豬八戒 | 12 |
| 沙僧 | 3 |
| 沙僧 | 5 |
| 孫悟空 | 1 |
| 孫悟空 | 20 |
| 唐僧 | 10 |
| 唐僧 | 17 |
+-----------+-----------+
9 rows in set (0.00 sec)
mysql> select a.user_name,sum(c.kills) from a join c on a.id = c.user_id group by a.user_name;
B +-----------+--------------+
| user_name | sum(c.kills) |
+-----------+--------------+
| 唐僧 | 27 |
| 孫悟空 | 21 |
| 沙僧 | 8 |
| 豬八戒 | 24 |
+-----------+--------------+
4 rows in set (0.00 sec)
結果1 使用Cross Join—————交叉連線(類笛卡爾積的方法)
mysql> select * from (select sum(c.kills) as '孫悟空' from a join c on a.id = c.user_id and a.user_name='孫悟空' gr by a.user_name) a cross join (select sum(c.kills) as '豬八戒' from a join c on a.id = c.user_id and a.user_name='豬八戒' group by a.user_name) b cross join (select sum(c.kills) as '沙僧' from a join c on a.id = c.user_id and a.user_name='沙僧' group by a.user_name) c;
C +-----------+-----------+--------+
| 孫悟空 | 豬八戒 | 沙僧 |
+-----------+-----------+--------+
| 21 | 24 | 8 |
+-----------+-----------+--------+
1 row in set (0.00 sec)
結果2 使用case case when 條件 then 獲取結果 end
mysql> select sum(case when a.user_name='豬八戒' then c.kills end) as 豬八戒, sum(case when a.user_name='沙僧' then c.kills end) as 沙僧,sum(case when a.user_name='孫悟空' then c.kills end) as 孫悟空 from a join c on a.id = c.user_id;
C +-----------+--------+-----------+
| 豬八戒 | 沙僧 | 孫悟空 |
+-----------+--------+-----------+
| 24 | 8 | 21 |
+-----------+--------+-----------+
1 row in set (0.00 sec)
單列轉多行場景介紹
mysql> select a.user_name,replace(substring(substring_index(mobile,',',a.id),char_length(substring_index(mobile,',',a.id-1))+1),',','') as mobile from tb_sequence a cross join (select a.user_name,concat(mobile,',')as mobile,length(mobile)-length(replace(mobile,',',''))+1 size from a) a on a.id <= b.size;
mysql> select a.user_name,concat(over,',')as mobile,length(over)-length(replace(over,',',''))+1 size from a;
+-----------+--------------------------------------------+------+
| user_name | mobile | size |
+-----------+--------------------------------------------+------+
| 唐僧 | 213312,31231231,32121312321, | 3 |
| 豬八戒 | 321324213,3214213, | 2 |
| 孫悟空 | 534223423,423523321,3242342342,432423423, | 4 |
| 沙僧 | 543123124,654654543, | 2 |
+-----------+--------------------------------------------+------+
4 rows in set (0.00 sec)
SQL: coalesce()函式
①用途:
將空值替換成其他值
返回第一個非空值
②表示式:
COALESCE是一個函式, (expression_1, expression_2, ...,expression_n)依次參考各引數表示式,遇到非null值即停止並返回該值。如果所有的表示式都是空值,最終將返回一個空值。使用COALESCE在於大部分包含空值的表示式最終將返回空值。
coalesce(item1,item2,item3)
依次判斷item值,返回第一個不為null的值,若全為null,則返回null;
③例項:
mysql> select a.id,a.user_name,a.over,coalesce(b.id,'無') as id,coalesce(b.user_name,'佚名') as 姓名, coalesce(b.over,'無業') as 職業 from a left join b on a.user_name = b.user_name;
+----+-----------+-------------------------------------------+------+-----------+--------------+
| id | user_name | over | id | 姓名 | 職業 |
+----+-----------+-------------------------------------------+------+-----------+--------------+
| 3 | 孫悟空 | 534223423,423523321,3242342342,432423423, | 1 | 孫悟空 | 齊天大聖 |
| 1 | 唐僧 | 213312,31231231,32121312321, | 無 | 佚名 | 無業 |
| 2 | 豬八戒 | 321324213,3214213, | 無 | 佚名 | 無業 |
| 4 | 沙僧 | 543123124,654654543, | 無 | 佚名 | 無業 |
+----+-----------+-------------------------------------------+------+-----------+--------------+
4 rows in set (0.00 sec)
二、如何生成唯一序列號(常見於票據的生成或者主鍵的生成)
需要使用唯一序列號的場景
1.資料庫的主鍵
主鍵選擇時 儘可能的要小、唯一不重複
2.業務序列號
發票、車票、訂單
序列號的生成
1. MySQL中 : AUTO_INCERMENT
2. SQLServer: IDENTITY/SEQUENCE
3. Orale : SEQUENCE
4. PgSql : SEQUENCE
如何選擇生成序列號的方式
1. 優先選擇系統提供的序列號選擇方式 (缺點是:容易產生空洞、在事物操作插入之後回滾 會佔用序列號)
2. 在特殊情況下可以使用SQL方式生成序列號
如何使用SQL語句建立特殊需求的序列號
需求:生成訂單的序列號,並且訂單格式如下
YYYYMMDDHHMMSSS。如201807200000002
DECLARE v_cnt INT;
DECLARE v_timestr INT;
DECLARE v_timestr = DATE_FORMAT(NOW(),'%Y%m%d');
SELECT ROUND(RAND()*100,0)+1 INTO v_cnt;
START TRANSACTION;
UPDATE order_seq(timestr,order_sn)+v_cnt WHERE timestr = v_timestr;
IF ROW_COUNT() = 0 THEN
INSERT INTO order_seq(timestr,order_sn) VALUES(v_timestr,v_cnt);
END IF;
SELECT CONCAT(v_timestr,LPAD(order_sn,7,0)) AS order_sn
FROM order_seq WHERE timestr = v_timestr;
COMMIT;
UPDATE order_seq SET order_sn = order_sn + v_cnt WHERE timestr = v_timestr;
IF ROW_COUNT() = 0 THEN
INSERT INTO order_seq(timestr,order_sn) VALUES(v_timestr,v_cnt);
END IF;
SELECT CONCAT(v_timestr,LPAD(order_sn,7,0))AS order_sn
FROM order_seq WHERE timestr = v_timestr;
COMMIT;
三、如何刪除重複資料
產生資料重複的原因
1. 人為原因、如重複錄入資料、重複提交 等
2. 系統原因、由於系統升級或設計的原因使原來可以重複的資料變為不能重複了。
如何查詢資料是否重複?
利用group by和having從句處理
sql資料格式顯示
mysql> select * from a;
+----+-----------+-------------------------------------------+
| id | user_name | over |
+----+-----------+-------------------------------------------+
| 1 | 唐僧 | 213312,31231231,32121312321, |
| 2 | 豬八戒 | 321324213,3214213, |
| 3 | 孫悟空 | 534223423,423523321,3242342342,432423423, |
| 4 | 沙僧 | 543123124,654654543, |
| 5 | 唐僧 | 321321313,321312 |
| 6 | 唐僧 | 321313,32321 |
+----+-----------+-------------------------------------------+
6 rows in set (0.00 sec)
1. 處理方式
採用Group by 與 Having 檢視資料是否重複
Having解釋: 如果需要對組函式的結果作為條件,那麼不能使用where子句,必須使用having子句。
寫法1.(檢視重複資料的數量)
mysql> select user_name,count(*) from a group by user_name having count(*) > 1;
+-----------+----------+
| user_name | count(*) |
+-----------+----------+
| 唐僧 | 3 |
+-----------+----------+
1 row in set (0.00 sec)
2. 刪除簡單重複資料,對於相同資料保留ID最大的
第一步:
mysql> select user_name,count(*) as 出現次數,max(id) as id from a GROUP BY user_name having count(*) > 1 ;
+-----------+--------------+------+
| user_name | 出現次數 | id |
+-----------+--------------+------+
| 唐僧 | 3 | 6 |
| 孫悟空 | 2 | 7 |
+-----------+--------------+------+
2 rows in set (0.00 sec)
第二步:
mysql> delete a from a join(select user_name,count(*),max(id) as id from a GROUP BY user_name having count(*) > 1) b on a.user_name = b.user_name where a.id < b.id;
Query OK, 3 rows affected (0.00 sec)
mysql> select * from a;
+----+-----------+-----------------+
| id | user_name | over |
+----+-----------+-----------------+
| 6 | 唐僧 | 321313,32321 |
| 7 | 孫悟空 | 321,321321 |
| 9 | 沙僧 | 423434,32432432 |
| 10 | 豬八戒 | 24324,43243223 |
+----+-----------+-----------------+
4 rows in set (0.00 sec)
3. 刪除複雜重複資料
mysql> select * from a;
重複的資料在 over列中的321
+----+-----------+----------------+
| id | user_name | over |
+----+-----------+----------------+
| 6 | 唐僧 | 321,32321 |
| 7 | 孫悟空 | 321,321321 |
| 9 | 沙僧 | 423434,321,321 |
| 10 | 豬八戒 | 24324,43243223 |
+----+-----------+----------------+
4 rows in set (0.00 sec)
刪除某個列中的重複資料