Oracle 高级分组用法详解
2017-09-29 本文已影响62人
Bobby0322
创建表及测试数据
CREATE TABLE STUDENTS
( ID NUMBER(15,0),
AREA VARCHAR2(10),
STU_TYPE VARCHAR2(2),
SCORE NUMBER(20,2));
INSERT INTO STUDENTS VALUES(1, '111', 'G', 80 );
INSERT INTO STUDENTS VALUES(1, '111', 'J', 80 );
INSERT INTO STUDENTS VALUES(1, '222', 'G', 89 );
INSERT INTO STUDENTS VALUES(1, '222', 'G', 68 );
INSERT INTO STUDENTS VALUES(2, '111', 'G', 80 );
INSERT INTO STUDENTS VALUES(2, '111', 'J', 70 );
INSERT INTO STUDENTS VALUES(2, '222', 'G', 60 );
INSERT INTO STUDENTS VALUES(2, '222', 'J', 65 );
INSERT INTO STUDENTS VALUES(3, '111', 'G', 75 );
INSERT INTO STUDENTS VALUES(3, '111', 'J', 58 );
INSERT INTO STUDENTS VALUES(3, '222', 'G', 58 );
INSERT INTO STUDENTS VALUES(3, '222', 'J', 90 );
INSERT INTO STUDENTS VALUES(4, '111', 'G', 89 );
INSERT INTO STUDENTS VALUES(4, '111', 'J', 90 );
INSERT INTO STUDENTS VALUES(4, '222', 'G', 90 );
INSERT INTO STUDENTS VALUES(4, '222', 'J', 89 );
COMMIT;
SELECT * FROM STUDENTS;
GROUPING SETS
类似于ROLLUP和CUBE
SELECT ID
,AREA
,STU_TYPE
,SUM(SCORE) SCORE
FROM STUDENTS
GROUP BY GROUPING SETS((ID,AREA,STU_TYPE),(ID,AREA),ID)
ORDER BY ID,AREA,STU_TYPE;
/*
ID AREA STU_TYPE SCORE
1 1 111 G 80
2 1 111 J 80
3 1 111 160
4 1 222 G 157
5 1 222 157
6 1 317
7 2 111 G 80
8 2 111 J 70
9 2 111 150
10 2 222 G 60
11 2 222 J 65
12 2 222 125
13 2 275
14 3 111 G 75
15 3 111 J 58
16 3 111 133
17 3 222 G 58
18 3 222 J 90
19 3 222 148
20 3 281
21 4 111 G 89
22 4 111 J 90
23 4 111 179
24 4 222 G 90
25 4 222 J 89
26 4 222 179
27 4 358*/
理解GROUPING SETS
/*理解GROUPING SETS
SELECT A, B, C, SUM( D ) FROM T
GROUP BY GROUPING SETS ( A, B, C )
等效于
SELECT * FROM (
SELECT A, NULL, NULL, SUM( D ) FROM T GROUP BY A
UNION ALL
SELECT NULL, B, NULL, SUM( D ) FROM T GROUP BY B
UNION ALL
SELECT NULL, NULL, C, SUM( D ) FROM T GROUP BY C
*/
ROLLUP
ROLLUP 是GROUP BY 的扩展,它只能出现在GROUP BY后面。主要是为了解决多层分组的问题。
SELECT ID,AREA,STU_TYPE,SUM(SCORE) SCORE
FROM STUDENTS
GROUP BY ROLLUP(ID,AREA,STU_TYPE)
ORDER BY ID,AREA,STU_TYPE;
/*
ID AREA STU_TYPE SCORE
1 1 111 G 80
2 1 111 J 80
3 1 111 160
4 1 222 G 157
5 1 222 157
6 1 317
7 2 111 G 80
8 2 111 J 70
9 2 111 150
10 2 222 G 60
11 2 222 J 65
12 2 222 125
13 2 275
14 3 111 G 75
15 3 111 J 58
16 3 111 133
17 3 222 G 58
18 3 222 J 90
19 3 222 148
20 3 281
21 4 111 G 89
22 4 111 J 90
23 4 111 179
24 4 222 G 90
25 4 222 J 89
26 4 222 179
27 4 358
28 1231*/
理解ROLLUP
/*理解ROLLUP
SELECT A, B, C, SUM( D )
FROM T
GROUP BY ROLLUP(A, B, C);
等效于
SELECT * FROM (
SELECT A, B, C, SUM( D ) FROM T GROUP BY A, B, C
UNION ALL
SELECT A, B, NULL, SUM( D ) FROM T GROUP BY A, B
UNION ALL
SELECT A, NULL, NULL, SUM( D ) FROM T GROUP BY A
UNION ALL
SELECT NULL, NULL, NULL, SUM( D ) FROM T
)
*/
CUBE
CUBE类似于ROLLUP,但产生的分组是分组类表的排列组合
SELECT ID,AREA,STU_TYPE,SUM(SCORE) SCORE
FROM STUDENTS
GROUP BY CUBE(ID,AREA,STU_TYPE)
ORDER BY ID,AREA,STU_TYPE;
/*1 111 G 80
1 111 J 80
1 111 160
1 222 G 157
1 222 157
1 G 237
1 J 80
1 317
2 111 G 80
2 111 J 70
2 111 150
2 222 G 60
2 222 J 65
2 222 125
2 G 140
2 J 135
2 275
3 111 G 75
3 111 J 58
3 111 133
3 222 G 58
3 222 J 90
3 222 148
3 G 133
3 J 148
3 281
4 111 G 89
4 111 J 90
4 111 179
4 222 G 90
4 222 J 89
4 222 179
4 G 179
4 J 179
4 358
111 G 324
111 J 298
111 622
222 G 365
222 J 244
222 609
G 689
J 542
1231*/
理解CUBE
/*理解CUBE
SELECT A, B, C, SUM( D ) FROM T
GROUP BY CUBE( A, B, C)
等效于
SELECT A, B, C, SUM( D ) FROM T
GROUP BY GROUPING SETS(
( A, B, C ),
( A, B ), ( A ), ( B, C ),
( B ), ( A, C ), ( C ), () )*/
GROUPING
从上面的结果中我们很容易发现,每个统计数据所对应的行都会出现NULL,
如何来区分到底是根据那个字段做的汇总呢,GROUPING函数判断是否合计列!
SELECT
DECODE(GROUPING(ID),1,'ALL ID',ID) ID
,DECODE(GROUPING(AREA),1,'ALL AREA',TO_CHAR(AREA)) AREA
,DECODE(GROUPING(STU_TYPE),1,'ALL_STU_TYPE',STU_TYPE) STU_TYPE
,SUM(SCORE) SCORE
FROM STUDENTS
GROUP BY CUBE(ID,AREA,STU_TYPE)
ORDER BY ID,AREA,STU_TYPE;
/*1 111 ALL_STU_TYPE 160
1 111 G 80
1 111 J 80
1 222 ALL_STU_TYPE 157
1 222 G 157
1 ALL AREA ALL_STU_TYPE 317
1 ALL AREA G 237
1 ALL AREA J 80
2 111 ALL_STU_TYPE 150
2 111 G 80
2 111 J 70
2 222 ALL_STU_TYPE 125
2 222 G 60
2 222 J 65
2 ALL AREA ALL_STU_TYPE 275
2 ALL AREA G 140
2 ALL AREA J 135
3 111 ALL_STU_TYPE 133
3 111 G 75
3 111 J 58
3 222 ALL_STU_TYPE 148
3 222 G 58*/
综合示例代码
CREATE TABLE T_DIST
(
TYPE_CD NUMBER,
BUYER_ID VARCHAR2(50),
ORDER_DT DATE,
SO_ID VARCHAR2(50) NOT NULL,
STOCK_ID VARCHAR2(50) NOT NULL,
UNIT_PRICE NUMBER,
DISCOUNT NUMBER,
QTY NUMBER
);
TRUNCATE TABLE T_DIST;
INSERT INTO T_DIST VALUES(1,'CN1001',TO_DATE('2008-04-01','YYYY-MM-DD'),'S9001','29110311',50,10,8);
INSERT INTO T_DIST VALUES(1,'CN1001',TO_DATE('2008-04-02','YYYY-MM-DD'),'S9002','29110312',60,20,2);
INSERT INTO T_DIST VALUES(1,'CN1001',TO_DATE('2008-04-03','YYYY-MM-DD'),'S9003','29110313',70,15,3);
INSERT INTO T_DIST VALUES(2,'CN1001',TO_DATE('2008-04-04','YYYY-MM-DD'),'S9004','29110312',60,15,5);
INSERT INTO T_DIST VALUES(2,'CN1001',TO_DATE('2008-04-05','YYYY-MM-DD'),'S9005','29110311',70,10,6);
INSERT INTO T_DIST VALUES(3,'CN1001',TO_DATE('2008-04-06','YYYY-MM-DD'),'S9006','29110313',55,20,4);
INSERT INTO T_DIST VALUES(3,'CN1001',TO_DATE('2008-04-06','YYYY-MM-DD'),'S9007','29110311',40,10,3);
INSERT INTO T_DIST VALUES(3,'CN1001',TO_DATE('2008-04-07','YYYY-MM-DD'),'S9008','29110312',50,50,5);
INSERT INTO T_DIST VALUES(3,'CN1001',TO_DATE('2008-04-07','YYYY-MM-DD'),'S9009','29110313',80,10,2);
INSERT INTO T_DIST VALUES(1,'CN1001',TO_DATE('2008-04-08','YYYY-MM-DD'),'S9010','29110311',65,10,1);
COMMIT;
SELECT * FROM T_DIST;
/*请问:如何实现如下结果,谢谢!
即计算按STOCK_ID,TYPE_CD,DISTOUNT分组,计算每个产品的销售额(QTY*UNIT_PRICE)及销售百分比,并有小计
STOCK_ID TYPE_CD DISCOUNT AVG_PRICE SUM_TOT PCT
-------------------------------------------------------------------------------------------------
29110311 1 10 57.50 465 46.27%
29110311 2 10 70.00 420 41.79%
29110311 3 10 40.00 120 11.94%
小计 55.83 1005 100.00%
29110312 1 20 60.00 120 17.91%
29110312 2 15 60.00 300 44.78%
29110312 3 50 50.00 250 37.31%
小计 56.67 670 100.00%
29110313 1 15 70.00 210 35.59%
29110313 3 10 80.00 160 27.12%
29110313 3 20 55.00 220 37.29%
小计 68.33 590 100.00%
*/
SELECT DECODE(RN, 1, '小计', STOCK_ID) STOCK_ID
,TYPE_CD
,DISCOUNT
,AVG_PRICE
,SUM_TOT
,ROUND(NVL(RATIO_TO_REPORT(DECODE(RN, 0, SUM_TOT, NULL)) OVER(PARTITION BY STOCK_ID), 1) * 100, 2) || '%' PCT
FROM
( SELECT STOCK_ID
,TYPE_CD
,DISCOUNT
,ROUND(AVG(UNIT_PRICE), 2) AVG_PRICE
,SUM(QTY * UNIT_PRICE) SUM_TOT
,GROUPING(TYPE_CD) RN -- GROUPING函数返回的是0或者1
,GROUP_ID() GROUP_ID -- GROUP_ID 分组可以识别哪些是重复的分组,唯一的分组返回0
FROM T_DIST
GROUP BY GROUPING SETS((STOCK_ID), (STOCK_ID, TYPE_CD, DISCOUNT)) );