Oracle Analytic関数は、現在の行の計算を実行するために使用される行の範囲を決定するwindowと呼ばれる行のグループに基づいて集計値を計算します。最もよく使用される分析関数は次のとおりです。
– RANK、DENSE_RANK、ROW_NUMBER
–LAGとLEAD
–FIRST_VALUEとLAST_VALUE
RANK、DENSE_RANK、ROW_NUMBERの分析関数について説明します。これらは本質的に非常に似ているため、要件に基づいて使用する必要があります。また、それらの違いについても説明します。
一般的な構文は次のとおりです
analytic_function([ arguments ]) OVER ([ query_partition_clause ] [ order_by_clause ])
OracleのROW_NUMBER関数
ROW_NUMBER order_by_clauseで指定された行の順序付けられたシーケンスで、同じウィンドウの各行に一意の番号を割り当てます。
まずサンプルデータを作成しましょう
CREATE TABLE "DEPT" ( "DEPTNO" NUMBER(2,0), "DNAME" VARCHAR2(14), "LOC" VARCHAR2(13), CONSTRAINT "PK_DEPT" PRIMARY KEY ("DEPTNO") ) CREATE TABLE "EMP" ( "EMPNO" NUMBER(4,0), "ENAME" VARCHAR2(10), "JOB" VARCHAR2(9), "MGR" NUMBER(4,0), "HIREDATE" DATE, "SAL" NUMBER(7,2), "COMM" NUMBER(7,2), "DEPTNO" NUMBER(2,0), CONSTRAINT "PK_EMP" PRIMARY KEY ("EMPNO"), CONSTRAINT "FK_DEPTNO" FOREIGN KEY ("DEPTNO") REFERENCES "DEPT" ("DEPTNO") ENABLE ); SQL> desc emp Name Null? Type ---- ---- ----- EMPNO NOT NULL NUMBER(4) ENAME VARCHAR2(10) JOB VARCHAR2(9) MGR NUMBER(4) HIREDATE DATE SAL NUMBER(7,2) COMM NUMBER(7,2) DEPTNO NUMBER(2) SQL> desc dept Name Null? Type ---- ----- ---- DEPTNO NOT NULL NUMBER(2) DNAME VARCHAR2(14) LOC VARCHAR2(13) insert into DEPT values(10, 'ACCOUNTING', 'NEW YORK'); insert into dept values(20, 'RESEARCH', 'DALLAS'); insert into dept values(30, 'RESEARCH', 'DELHI'); insert into dept values(40, 'RESEARCH', 'MUMBAI'); commit; insert into emp values( 7839, 'Clark', 'MANAGER', 7839, to_date('9-6-2008','dd-mm-yyyy'), 28573, null, 10 ); insert into emp values( 7782, 'Clara', 'MANAGER', 7839, to_date('9-6-2008','dd-mm-yyyy'), 0, null, 10 ); insert into emp values( 7934, 'Blake', 'MANAGER', 7839, to_date('1-5-2007','dd-mm-yyyy'), 0, null, 10 ); insert into emp values( 7788, 'Scott', 'ANALYST', 7788, to_date('9-6-2012','dd-mm-yyyy'), 30000, null, 20 ); insert into emp values( 7902, 'Bill', 'ANALYST', 7832, to_date('9-6-2012','dd-mm-yyyy'), 30000, null, 20 ); insert into emp values( 7876, 'TPM', 'ANALYST', 7566, to_date('9-6-2017','dd-mm-yyyy'), 11000, null, 20 ); insert into emp values( 7369, 'TPM1', 'ANALYST', 7566, to_date('9-6-2017','dd-mm-yyyy'), 8000, null, 20 ); insert into emp values( 7698, 'A1', 'ANALYST', 7788, to_date('9-6-2017','dd-mm-yyyy'), 28500, null, 30 ); insert into emp values( 7499, 'A2', 'ANALYST', 7698, to_date('9-7-2017','dd-mm-yyyy'), 16000, null, 30 ); insert into emp values( 7844, 'A3', 'ANALYST', 7698, to_date('9-7-2017','dd-mm-yyyy'), 15000, null, 30 ); insert into emp values( 7654, 'A4', 'ANALYST', 7698, to_date('9-7-2017','dd-mm-yyyy'), 12500, null, 30 ); insert into emp values( 7521, 'A5', 'ANALYST', 7698, to_date('9-7-2017','dd-mm-yyyy'), 12500, null, 30 ); insert into emp values( 7900, 'A6', 'ANALYST', 77698, to_date('9-7-2017','dd-mm-yyyy'), 0, null, 30 ); commit;
SQL> desc emp Name Null? Type ----------------------------------------- -------- ---------------------------- EMPNO NOT NULL NUMBER(4) ENAME VARCHAR2(10) JOB VARCHAR2(9) MGR NUMBER(4) HIREDATE DATE SAL NUMBER(7,2) COMM NUMBER(7,2) DEPTNO NUMBER(2) SQL> select deptno ,count(*) from emp group by deptno; DEPTNO COUNT(*) ---------- ---------- 30 6 20 4 10 3 SQL> select deptno, ename, sal, row_number() over (partition by deptno order by sal) "row_number" from emp; DEPTNO ENAME SAL row_number ---------- ---------- ---------- ---------- 10 CLARK 0 1 10 MILLER 0 2 10 allen 28573 3 20 SMITH 8000 1 20 ADAMS 11000 2 20 SCOTT 30000 3 20 FORD 30000 4 30 JAMES 9500 1 30 MARTIN 12500 2 30 WARD 12500 3 30 TURNER 15000 4 30 ALLEN 16000 5 30 BLAKE 28500 6 13 rows selected.
OracleのRANK関数
ランク ROW_NUMBERとほぼ同じですが、同じウィンドウ内で、order by句が指定されている値が等しい行は同じランクを受け取りますが、次の行はROW_NUMBERに従ってRANKを受け取ります。
SQL> select deptno, ename, sal, rank() over (partition by deptno order by sal) "RANK" from emp; DEPTNO ENAME SAL RANK ---------- ---------- ---------- ---------- 10 CLARK 0 1 10 MILLER 0 2 10 allen 28573 3 20 SMITH 8000 1 20 ADAMS 11000 2 20 SCOTT 30000 3 20 FORD 30000 3 30 JAMES 9500 1 30 MARTIN 12500 2 30 WARD 12500 2 30 TURNER 15000 4 30 ALLEN 16000 5 30 BLAKE 28500 6 13 rows selected.
OracleのDense_rank関数
DENSE_RANK RANKとほぼ同じですが、1つ以上の値が同じである場合、行間にギャップは残りません。次の例のように、同じグループのWARDの横にあるTURNERはDENSE_RANK3を受け取ります。
SQL> select deptno, ename, sal, dense_rank() over (partition by deptno order by sal) "DENSE_RANK" from emp; DEPTNO ENAME SAL DENSE_RANK ---------- ---------- ---------- ---------- 10 CLARK 0 1 10 MILLER 0 2 10 allen 28573 3 20 SMITH 8000 1 20 ADAMS 11000 2 20 SCOTT 30000 3 20 FORD 30000 3 30 JAMES 9500 1 30 MARTIN 12500 2 30 WARD 12500 2 30 TURNER 15000 3 30 ALLEN 16000 4 30 BLAKE 28500 5 13 rows selected.
3つすべてを1つのクエリに入れることもできます
select deptno, ename, sal, row_number() over (partition by deptno order by sal) "row_number", rank() over (partition by deptno order by sal) "rank", dense_rank() over (partition by deptno order by sal) "dense_rank" from emp; DEPTNO ENAME SAL row_number rank dense_rank ---------- ---------- ---------- ---------- ---------- ---------- 10 CLARK 0 1 1 1 10 MILLER 0 2 1 1 10 allen 28573 3 3 2 20 SMITH 8000 1 1 1 20 ADAMS 11000 2 2 2 20 SCOTT 30000 3 3 3 20 FORD 30000 4 3 3 30 JAMES 9500 1 1 1 30 MARTIN 12500 2 2 2 30 WARD 12500 3 2 2 30 TURNER 15000 4 4 3 30 ALLEN 16000 5 5 4 30 BLAKE 28500 6 6 5 13 rows selected.>
重複する行を削除するには、Row_number関数とRANK関数を使用できます
delete from t where rowid IN ( select rid from (select rowid rid, row_number() over (partition by column_name order by rowid) rn from t) where rn <> 1);
これらの関数は、上位Nおよび下位Nのクエリに非常に役立ちます。
以下のSQLを使用して、各部門の最高給与を見つけることができます
SQL> select * (select deptno, ename, sal, row_number() over (partition by deptno order by sal) "row_number" from emp ) where row_number=1;
Oracle Analytic関数のようなRANK、DENSE_RANK、ROW_NUMBERの説明と、クエリでデータを分析する方法について説明していただければ幸いです。クエリでこれらの関数を使用するときは、細心の注意を払う必要があります。そうしないと、結果が異なります。
関連記事
OracleのLEAD関数
Oracleの分析関数
Oracleインタビューの質問
Oracle集合演算子
OracleSqlチュートリアル
高密度ランクのOracleドキュメント