Oracle Pivot/Unpivot 此特性只适用于Oracle 11g 以上版本 CREATE TABLE CUSTOMERS ( CUST_ID NUMBER(10), CUST_NAME VARCHAR2(20), STATE_CODE VARCHAR2(2), TIMES_PURCHASED NUMBER(3) ); INSERT INTO CUSTOMERS VALUES(1,'ANKER','CT',1); INSERT INTO CUSTOMERS VALUES(1,'ANKER','ET',2); INSERT INTO CUSTOMERS VALUES(1,'ANKER','DT',3); INSERT INTO CUSTOMERS VALUES(1,'ANKER','QD',4); INSERT INTO CUSTOMERS VALUES(2,'JONH','ET',2); INSERT INTO CUSTOMERS VALUES(2,'JONH','DT',3); INSERT INTO CUSTOMERS VALUES(2,'JONH','QD',2); INSERT INTO CUSTOMERS VALUES(3,'QIANG','DT',3); INSERT INTO CUSTOMERS VALUES(3,'QIANG','QD',4); COMMIT; --该记录显示了客户所在的州以及该客户在商店购物的次数。当该客户从商店购买更多物品时,列 times_purchased 会进行更新。 SQL> SELECT * FROM CUSTOMERS; CUST_ID CUST_NAME ST TIMES_PURCHASED ---------- -------------------- -- --------------- 1 Anker CT 1 1 Anker ET 2 1 Anker DT 3 1 Anker QD 4 2 Jonh ET 2 2 Jonh DT 3 2 Jonh QD 2 3 Qiang DT 3 3 Qiang QD 4 已选择9行。 PIVOT用法 --1.查看每个州,每个客户的购买频率 SELECT * FROM CUSTOMERS PIVOT( SUM(TIMES_PURCHASED) FOR STATE_CODE IN ('QD' AS "New York", 'CT' AS "Connecticut", 'ET' AS "New Jersey", 'DT' AS "Missouri") ); --从结果可以看出,pivot将针对非pivot涉及的字段进行group by,pivot中的 in (...)子句中的值将作为列,聚合的值将作为列的值 CUST_ID CUST_NAME New York Connecticut New Jersey Missouri ---------- -------------------- ---------- ----------- ---------- ---------- 1 Anker 4 1 2 3 2 Jonh 2 2 3 3 Qiang 4 3 --2.了解各个州的购买频率,即,各个州有多少客户只购物一次、两次、三次等等。如果使用常规 SQL SQL> SELECT STATE_CODE, TIMES_PURCHASED, COUNT(*) 2 FROM CUSTOMERS 3 GROUP BY STATE_CODE, TIMES_PURCHASED; ST TIMES_PURCHASED COUNT(*) -- --------------- ---------- QD 4 2 QD 2 1 CT 1 1 ET 2 2 DT 3 3 --这样看起来不太直观 SELECT * FROM ( SELECT TIMES_PURCHASED, STATE_CODE FROM CUSTOMERS) T PIVOT( COUNT(STATE_CODE) FOR STATE_CODE IN ('QD', 'CT', 'ET', 'DT') ) TIMES_PURCHASED 'QD' 'CT' 'ET' 'DT' --------------- ---------- ---------- ---------- ---------- 1 0 1 0 0 2 1 0 2 0 4 2 0 0 0 3 0 0 0 3 --但是假设您希望显示州名而非缩写 SELECT * FROM ( SELECT TIMES_PURCHASED as "Puchase Frequency", STATE_CODE FROM CUSTOMERS) T PIVOT(COUNT(STATE_CODE) as CNT FOR STATE_CODE IN ('QD' AS "New York", 'CT' AS "Connecticut", 'ET' AS "New Jersey", 'DT' AS "Missouri") ); Puchase Frequency New York_CNT Connecticut_CNT New Jersey_CNT Missouri_CNT ----------------- ------------ --------------- -------------- ------------ 1 0 1 0 0 2 1 0 2 0 4 2 0 0 0 3 0 0 0 3 ----------------------------------------------------------------------------------------------------------------------------------- Unpivot 讲解 --1.将上面的pivot后的结果进行反转回来 CREATE TABLE TAB_MATRIX1 AS SELECT * FROM CUSTOMERS PIVOT( SUM(TIMES_PURCHASED) FOR STATE_CODE IN ('QD' AS "NEW YORK", 'CT' AS "CONNECTICUT", 'ET' AS "NEW JERSEY", 'DT' AS "MISSOURI") ); --查询表 SQL> SELECT * FROM TAB_MATRIX1 2 ; CUST_ID CUST_NAME New York Connecticut New Jersey Missouri ---------- -------------------- ---------- ----------- ---------- ---------- 1 Anker 4 1 2 3 2 Jonh 2 2 3 3 Qiang 4 3 --从结果中可以看出,for .. in (..)中的列名将作为state_code列的值,字段state_counts中的列将由原来的列值来代替 SQL> SELECT * FROM TAB_MATRIX1 2 unpivot 3 ( 4 state_counts 5 for state_code in ("New York","Connecticut","New Jersey","Missouri") 6 ); CUST_ID CUST_NAME STATE_CODE STATE_COUNTS ---------- -------------------- ----------- ------------ 1 Anker New York 4 1 Anker Connecticut 1 1 Anker New Jersey 2 1 Anker Missouri 3 2 Jonh New York 2 2 Jonh New Jersey 2 2 Jonh Missouri 3 3 Qiang New York 4 3 Qiang Missouri 3 已选择9行。 --将上面第二个结果也进行反转 CREATE TABLE TAB_MATRIX2 AS SELECT * FROM ( SELECT TIMES_PURCHASED as "Puchase Frequency", STATE_CODE FROM CUSTOMERS) T PIVOT(COUNT(STATE_CODE) as CNT FOR STATE_CODE IN ('QD' AS "New York", 'CT' AS "Connecticut", 'ET' AS "New Jersey", 'DT' AS "Missouri") ); SQL> SELECT * FROM TAB_MATRIX2; Puchase Frequency New York_CNT Connecticut_CNT New Jersey_CNT Missouri_CNT ----------------- ------------ --------------- -------------- ------------ 1 0 1 0 0 2 1 0 2 0 4 2 0 0 0 3 0 0 0 3 SELECT * FROM TAB_MATRIX2 UNPIVOT ( CNT_STATE_CODE FOR STATE_CODE IN ("New York_CNT","Connecticut_CNT","New Jersey_CNT","Missouri_CNT") ); Puchase Frequency STATE_CODE CNT_STATE_CODE ----------------- --------------- -------------- 1 New York_CNT 0 1 Connecticut_CNT 1 1 New Jersey_CNT 0 1 Missouri_CNT 0 2 New York_CNT 1 2 Connecticut_CNT 0 2 New Jersey_CNT 2 2 Missouri_CNT 0 4 New York_CNT 2 4 Connecticut_CNT 0 4 New Jersey_CNT 0 4 Missouri_CNT 0 3 New York_CNT 0 3 Connecticut_CNT 0 3 New Jersey_CNT 0 3 Missouri_CNT 3 --------------------------------------------------------------------------------------------------------------------- Oracle 11g之前的版本如何实现行转列及列转行 CREATE TABLE T AS SELECT * FROM ( SELECT OWNER,OBJECT_TYPE,COUNT(*) AS CNT FROM DBA_OBJECTS GROUP BY OWNER,OBJECT_TYPE ) WHERE ROWNUM <= 3; admin@ORCL> SELECT * FROM T; OWNER OBJECT_TYPE CNT ------------------------------ ------------------- ---------- BI SYNONYM 8 HR VIEW 1 HR INDEX 19 --列转行 admin@ORCL> SELECT OWNER, 2 SUM(CASE 3 WHEN OBJECT_TYPE = 'SYNONYM' THEN 4 CNT 5 ELSE 6 0 7 END) AS TYPE_SYNONYM, 8 SUM(CASE 9 WHEN OBJECT_TYPE = 'VIEW' THEN 10 CNT 11 ELSE 12 0 13 END) AS TYPE_VIEW, 14 SUM(CASE 15 WHEN OBJECT_TYPE = 'INDEX' THEN 16 CNT 17 ELSE 18 0 19 END) AS TYPE_INDEX 20 FROM T 21 GROUP BY OWNER; OWNER TYPE_SYNONYM TYPE_VIEW TYPE_INDEX ------------------------------ ------------ ---------- ---------- HR 0 1 19 BI 8 0 0 --列转行 DROP TABLE T PURGE; CREATE TABLE T ( OWNER VARCHAR2(10), TYPE_SYNONYM INT, TYPE_VIEW INT, TYPE_INDEX INT ); INSERT INTO T VALUES('HR','0','1','19'); INSERT INTO T VALUES('BI','8','0','0'); COMMIT; --查看数据 admin@ORCL> SELECT * FROM T; OWNER TYPE_SYNONYM TYPE_VIEW TYPE_INDEX ---------- ------------ ---------- ---------- HR 0 1 19 BI 8 0 0 --列转行 admin@ORCL> SELECT OWNER, 'SYNONYM' AS TYPE, TYPE_SYNONYM 2 FROM T 3 UNION ALL 4 SELECT OWNER, 'VIEW', TYPE_VIEW 5 FROM T 6 UNION ALL 7 SELECT OWNER, 'INDEX', TYPE_INDEX FROM T; OWNER TYPE TYPE_SYNONYM ---------- ------- ------------ HR SYNONYM 0 BI SYNONYM 8 HR VIEW 1 BI VIEW 0 HR INDEX 19 BI INDEX 0