mysql查詢:行轉列,列轉行,請不要再羞辱我了

被一道行轉列的sql面試題羞辱了,好傷心…
大概有這麼一個場景,有三個產品,分別是1,2,3,和三個倉庫,分別是01,02,03,三個倉庫分別儲存三個產品,數量分佈如下圖:

產品(PID) 倉庫(SID) 數量(PNUM)
1 01 10
1 02 8
2 02 11
2 03 5
3 03 5

想得到的結果如下圖:

產品(PID) 倉庫一 (S1ID) 倉庫二(S2ID) 倉庫三(S3ID)
1 10 8 0
2 0 11 5
3 0 0 5

請寫出sql?
這是一個典型的行轉列的問題,
創建表

CREATE TABLE `product_store_count` (
  `PID` varchar(11) DEFAULT NULL,
  `SID` varchar(11) DEFAULT NULL,
  `PNUM` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入測試數據


INSERT INTO product_store_count (PID,SID,PNUM) VALUES('1','01',10);
INSERT INTO product_store_count (PID,SID,PNUM) VALUES('1','02',20);
INSERT INTO product_store_count (PID,SID,PNUM) VALUES('1','03',10);
INSERT INTO product_store_count (PID,SID,PNUM) VALUES('2','01',25);
INSERT INTO product_store_count (PID,SID,PNUM) VALUES('2','02',16);
INSERT INTO product_store_count (PID,SID,PNUM) VALUES('3','03',12);

QzSzoF.png

我是這樣寫的,:

SELECT M.PID,
CASE WHEN M.sum is null then 0 ELSE M.sum END S1ID,
CASE WHEN N.sum is null then 0 ELSE N.sum END S2ID,
CASE WHEN P.sum is null then 0 ELSE P.sum END S3ID
FROM (SELECT PID,SID, SUM(PNUM) sum FROM product_store_count WHERE SID='01' GROUP BY PID,SID) M
LEFT JOIN (SELECT PID,SID, SUM(PNUM) sum FROM product_store_count WHERE SID='02' GROUP BY PID,SID) N
ON M.PID=N.PID
LEFT JOIN (SELECT PID,SID, SUM(PNUM) sum FROM product_store_count WHERE SID='03' GROUP BY PID,SID) P
ON M.PID=P.PID

QzECKH.png
其實仔細想想我這樣寫其實是有問題的,以目前的測試數據,查詢結果如下,只有倉庫01、02的數據,並沒有統計出03倉庫,事實上03倉庫是存有3號產品的。
爲什麼會有問題呢?仔細看一下測試數據可以發現,01倉庫有1、2兩種產品,02倉庫有1、2兩種產品,03倉庫只有3,我寫的sql還是左連接,向左匹配的結果,就會只有1、2兩種產品在01、02、03倉庫的庫存分佈情況,不會有3號產品的。

執行一下這條sqlINSERT INTO product_store_count (PID,SID,PNUM) VALUES(‘3’,‘01’,16);,讓01倉庫有1、2、3三種產品,再執行sql就會是正確的結果。

QzV16e.png
其實這麼寫最大問題就是有時候有可能執行是對的,有時候執行可能結果就是不對,漏掉有數據。生產中千萬不能這麼寫,最好的寫法應該是下面的這種寫法:

行轉列:

SELECT PID,
CASE SID WHEN '01' THEN PNUM ELSE 0 END S1ID,
CASE SID WHEN '02' THEN PNUM ELSE 0 END S2ID,
CASE SID WHEN '03' THEN PNUM ELSE 0 END S3ID
FROM product_store_count;

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wSgZFNXR-1577069913815)(https://s2.ax1x.com/2019/12/22/QzpKWd.png)]

SELECT PID,
MAX(CASE SID WHEN '01' THEN PNUM ELSE 0 END ) S1ID ,
MAX(CASE SID WHEN '02' THEN PNUM ELSE 0 END ) S2ID,
MAX(CASE SID WHEN '03' THEN PNUM ELSE 0 END ) S3ID
FROM product_store_count GROUP BY PID;

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DYAROmD2-1577069913815)(https://s2.ax1x.com/2019/12/22/Qzp3OP.png)]

那麼列轉行怎麼寫呢,看下面:

CREATE TABLE `product_store_count_2` (
  `PID` varchar(11) DEFAULT NULL,
  `S1ID` varchar(11) DEFAULT NULL,
  `S2ID` varchar(11) DEFAULT NULL,
   `S3ID` varchar(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `product_store_count_2` (`PID`, `S1ID`, `S2ID`, `S3ID`) VALUES ('1', '10', '20', '10');
INSERT INTO `product_store_count_2` (`PID`, `S1ID`, `S2ID`, `S3ID`) VALUES ('2', '25', '16', '0');
INSERT INTO `product_store_count_2` (`PID`, `S1ID`, `S2ID`, `S3ID`) VALUES ('3', '0', '0', '12');

QzpJw8.png

SELECT PID, '01' SID,S1ID PNUM FROM product_store_count_2 WHERE S1ID>0
UNION 
SELECT PID, '02' SID,S2ID PNUM FROM product_store_count_2 WHERE S2ID>0
UNION
SELECT PID, '03' SID,S3ID PNUM FROM product_store_count_2 WHERE S3ID>0
ORDER BY PID,SID ASC 

QzpwSs.png

聰明人一看就明白了,其實這都是運用一些技巧分步實現了要查詢的結果,很簡單

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章