0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

sql oracle rollup, cube, grouping_id, grouping_sets 【マトリクス作成】

Last updated at Posted at 2018-09-02

まえがき

rollup, cube, grouping_id, grouping_setsを使用して
マトリクスをどこまで作成できるか挑戦したいと思い、実践します。

元データ

SCOTT/TIGERでやってみようと思います。

image.png

grouping_idについて

grouping_idの返却値は
引数に指定した列を集約単位として扱わない場合に1をたたせ、
それ以外を0にねかせた値を10進数に変換した値です。

集約単位に応じて集約キーが表示されなくなる列を考えて書いています。
表示されなくなる列は1を立てます。

部署・jobごとに集約するのであれば、どちらも表示します。
0*2^1+0*2^0⇒0+0⇒0

部署ごとに集約するのであれば、
どのjobを表示するかは気にしていないので、jobは表示しません。
0*2^1+1*2^0⇒0*2+1*1⇒0+1⇒1

jobごとに集約するのであれば、
どの部署を表示するかは気にしていないので、部署は表示しません。
1*2^1+0*2^0⇒1*2+0*1⇒2+0⇒2

全体で集約するのであれば、
どの部署・どのjobを表示するかは気にしていないので、
jobも部署も表示しません。
1*2^1+1*2^0⇒1*2+1*1⇒2+1⇒3

grouping_setsの場合

grouping_sets.sql

SELECT
    CASE
        WHEN GROUPING_ID(deptno,job) = '0' THEN '部署jobごと'--(0+0)
        WHEN GROUPING_ID(deptno,job) = '1' THEN '部署ごと'--(0+1)
        WHEN GROUPING_ID(deptno,job) = '2' THEN 'jobごと'--(1+0)
        WHEN GROUPING_ID(deptno,job) = '3' THEN '全体'--(1+1)
    END AS 集約単位
    ,GROUPING_ID(deptno,job)
    ,deptno
    ,job
    ,SUM(sal)
FROM
    emp
GROUP BY
    GROUPING SETS (
                    (deptno,job)--部署job単位で集約
                    ,(deptno)--部署単位で集約
                    ,(job)--job単位で集約
                    ,()--全量で集約
                    )
ORDER BY
    deptno
    ,job
;

【grouping_setsの取得結果】

image.png

rollupの場合①

rollup、指定した列の順序気にします。。

部署単位表示されません。

GROUPING_ID(deptno,job)も1の値はありません。

rollup_1.sql

SELECT
    CASE
        WHEN GROUPING_ID(deptno,job) = '0' THEN '部署jobごと'--(0+0)
        WHEN GROUPING_ID(deptno,job) = '1' THEN '部署ごと'--(0+1)
        WHEN GROUPING_ID(deptno,job) = '2' THEN 'jobごと'--(1+0)
        WHEN GROUPING_ID(deptno,job) = '3' THEN '全体'--(1+1)
    END AS 集約単位
    ,GROUPING_ID(deptno,job)
    ,job
    ,deptno
    ,SUM(sal)
FROM
    emp
GROUP BY
    ROLLUP(job,deptno)
--以下と等価
--GROUPING SETS (
--				(DEPTNO,JOB)--部署JOB単位で集約
--				,(JOB)--JOB単位で集約
--				,()--全量で集約
--				)
ORDER BY
    job
    ,deptno
;

【rollup①の取得結果】
image.png

rollupの場合②

rollup、指定した列の順序気にします。。

job単位表示されません。

GROUPING_ID(deptno,job)も2の値はありません。

rollup_2.sql

SELECT
    CASE
        WHEN GROUPING_ID(deptno,job) = '0' THEN '部署jobごと'--(0+0)
        WHEN GROUPING_ID(deptno,job) = '1' THEN '部署ごと'--(0+1)
        WHEN GROUPING_ID(deptno,job) = '2' THEN 'jobごと'--(1+0)
        WHEN GROUPING_ID(deptno,job) = '3' THEN '全体'--(1+1)
    END AS 集約単位
    ,GROUPING_ID(deptno,job)
    ,job
    ,deptno
    ,SUM(sal)
FROM
    emp
GROUP BY
    ROLLUP(deptno,job)
--以下と等価
--    GROUPING SETS (
--                    (deptno,job)--部署job単位で集約
--                    ,(deptno)--部署単位で集約
--                    ,()--全量で集約
--                    )
ORDER BY
    deptno
    ,job
;

【rollup②の取得結果】
image.png

cubeの場合

cube、順序気にしません。。

cube.sql

SELECT
    CASE
        WHEN GROUPING_ID(deptno,job) = '0' THEN '部署jobごと'--(0+0)
        WHEN GROUPING_ID(deptno,job) = '1' THEN '部署ごと'--(0+1)
        WHEN GROUPING_ID(deptno,job) = '2' THEN 'jobごと'--(1+0)
        WHEN GROUPING_ID(deptno,job) = '3' THEN '全体'--(1+1)
    END AS 集約単位
    ,GROUPING_ID(deptno,job)
    ,job
    ,deptno
    ,SUM(sal)
FROM
    emp
GROUP BY
    CUBE(job,deptno)
--以下と等価
--    GROUPING SETS (
--                    (deptno,job)--部署job単位で集約
--                    ,(deptno)--部署単位で集約
--                    ,(job)--job単位で集約
--                    ,()--全量で集約
--                    )
ORDER BY
    deptno
    ,job
;


【cubeの取得結果】

image.png

やってみたかったこと

まえおき長くなりましたが、
こういうの作ってみたかったのです。。
image.png

つくります。

サマリ行とサマリ行以外で分けて考える・・
サマリ行とサマリ行以外をunion allでくっつける。。

【サマリ行以外】
サマリ行より上は、よくある縦持ちを横持ちにするやつです。
部署ごとの縦横変換ですので、部署がnullでないやつを
sub.deptno IS NOT NULLで抽出しています。

部署ごとの合計列を表示したいので、
sub.aggflg = '1' AND sub.JOB IS NULLで対象行を見つけにいっています。

同じ部署に属するJOBごとに列を設けたいので、
sub.aggflg = '0' AND sub.JOB = 'CLERK'
sub.aggflg = '0' AND sub.JOB = 'SALESMAN'
sub.aggflg = '0' AND sub.JOB = 'MANAGER'
sub.aggflg = '0' AND sub.JOB = 'ANALYST'
sub.aggflg = '0' AND sub.JOB = 'PRESIDENT'

のようにして、JOB数分、対象行を見つけにいっています。

【サマリ行】
サマリ行は各列の合計になっていてほしいので、
ここでの各列はJOBです。

sub.aggflg = '2' AND sub.JOB = 'CLERK'
sub.aggflg = '2' AND sub.JOB = 'SALESMAN'
sub.aggflg = '2' AND sub.JOB = 'MANAGER'
sub.aggflg = '2' AND sub.JOB = 'ANALYST'
sub.aggflg = '2' AND sub.JOB = 'PRESIDENT'

のようにして、JOB数分、対象行を見つけにいっています。
sub.aggflg = '2' はJOBごとの集計結果です。。

部署合計列に表示するのは、
JOBごとの集計結果と部署ごとの集計結果の足し算なので、

sub.aggflg = '3' AND sub.JOB IS NULL AND sub.deptno IS NULL

のようにして、対象行を見つけにいっています。

それと、集計単位が2、3の行しか使わないので、あらかじめ
sub.aggflg IN ('2','3')で対象行を絞っています。

matrix.sql

SELECT
    to_char(sub.deptno) AS deptno
    ,MAX(
            CASE
                WHEN sub.aggflg = '0' AND sub.JOB = 'CLERK' THEN sub.SUM
                ELSE NULL
            END) AS clerk
    ,MAX(
            CASE
                WHEN sub.aggflg = '0' AND sub.JOB = 'SALESMAN' THEN sub.SUM
                ELSE NULL
            END) AS salesman
    ,MAX(
            CASE
                WHEN sub.aggflg = '0' AND sub.JOB = 'MANAGER' THEN sub.SUM
                ELSE NULL
            END) AS MANAGER
    ,MAX(
            CASE
                WHEN sub.aggflg = '0' AND sub.JOB = 'ANALYST' THEN sub.SUM
                ELSE NULL
            END) AS analyst
    ,MAX(
            CASE
                WHEN sub.aggflg = '0' AND sub.JOB = 'PRESIDENT' THEN sub.SUM
                ELSE NULL
            END) AS president
    ,MAX(
            CASE
                WHEN sub.aggflg = '1' AND sub.JOB IS NULL THEN sub.SUM
                ELSE NULL
            END) AS 部署合計
FROM
    (
        SELECT
            CASE
                WHEN GROUPING_ID(deptno,JOB) = '0' THEN '部署jobごと'
                WHEN GROUPING_ID(deptno,JOB) = '1' THEN '部署ごと'
                WHEN GROUPING_ID(deptno,JOB) = '2' THEN 'jobごと'
                WHEN GROUPING_ID(deptno,JOB) = '3' THEN '全体'
            END AS 集約単位
            ,GROUPING_ID(deptno,JOB) AS aggflg
            ,deptno
            ,JOB
            ,SUM(sal) AS SUM
        FROM
            emp
        GROUP BY
            GROUPING SETS (
                            (deptno,JOB)--部署job単位で集約
                            ,(deptno)--部署単位で集約
                            ,(JOB)--job単位で集約
                            ,()--全量で集約
                            )
    ) sub
WHERE
    sub.deptno IS NOT NULL
GROUP BY
    sub.deptno
UNION ALL
SELECT
    'job合計'
    ,MAX(
            CASE
                WHEN sub.aggflg = '2' AND sub.JOB = 'CLERK' THEN sub.SUM
                ELSE NULL
            END) AS clerk
    ,MAX(
            CASE
                WHEN sub.aggflg = '2' AND sub.JOB = 'SALESMAN' THEN sub.SUM
                ELSE NULL
            END) AS salesman
    ,MAX(
            CASE
                WHEN sub.aggflg = '2' AND sub.JOB = 'MANAGER' THEN sub.SUM
                ELSE NULL
            END) AS MANAGER
    ,MAX(
            CASE
                WHEN sub.aggflg = '2' AND sub.JOB = 'ANALYST' THEN sub.SUM
                ELSE NULL
            END) AS analyst
    ,MAX(
            CASE
                WHEN sub.aggflg = '2' AND sub.JOB = 'PRESIDENT' THEN sub.SUM
                ELSE NULL
            END) AS president
    ,MAX(
            CASE
                WHEN sub.aggflg = '3' AND sub.JOB IS NULL AND sub.deptno IS NULL THEN sub.SUM
                ELSE NULL
            END) AS 合計
FROM
    (
        SELECT
            CASE
                WHEN GROUPING_ID(deptno,JOB) = '0' THEN '部署jobごと'
                WHEN GROUPING_ID(deptno,JOB) = '1' THEN '部署ごと'
                WHEN GROUPING_ID(deptno,JOB) = '2' THEN 'jobごと'
                WHEN GROUPING_ID(deptno,JOB) = '3' THEN '全体'
            END AS 集約単位
            ,GROUPING_ID(deptno,JOB) AS aggflg
            ,deptno
            ,JOB
            ,SUM(sal) AS SUM
        FROM
            emp
        GROUP BY
            GROUPING SETS (
                            (deptno,JOB)--部署job単位で集約
                            ,(deptno)--部署単位で集約
                            ,(JOB)--job単位で集約
                            ,()--全量で集約
                            )
    ) sub
WHERE
    sub.aggflg IN ('2','3')
ORDER BY
    deptno
;

所感

unpivotしたらどうなるんだろうと思いました。
たぶん、rollup①と②の結果に帰着するのではと思います。

暗黙のグループ化があるので、すこし、なれていないunpivot。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?