5段階でSQL再帰的CTEを書く方法を学んでください
23352 ワード
ノードの階層に従うグラフデータベースを必要としません.SQLは結合、自己結合、およびその前の結果に反復的に結合することもできます.この
最古のサンプルテーブルを作成します.
最初のステップはグラフのルートを識別することです.あなたは従業員によって開始し、自分のマネージャーを表示したり、マンガで起動し、その従業員を表示しますか?私は後者をします.階層の根元は管理者のないものです
CTE ( common table expression )に入れます
私のレベル0が定義されているので
だからここでは従業員はキングを参照してください:
マネージャ名を示すのは1ステップでしたが、階層全体を表示するには、すべてのレベルから行を連結する必要があります.私は2つの最初のレベルから始まります.私はすべての組合が必要です.このため、with句で自分のjoinクエリを移動し、
私は2つの最初のレベルの結果を検証したので、それを再帰的に取得する時間です.加わる代わりに
実行計画では、それを再帰的な組合として見る
WITH RECURSIVE
節はしばしば「再帰的なCTE」と呼ばれます.これは、開発者が手続き言語に使用する抽象化のビットを必要とする場合があります.ここではどのように“手動で”2つの最初のレベルに参加することによって説明している、再帰せずに、さらに“再帰”に移動します.最初の2つのレベルから始まる簡単にロジックを検証するのに役立ちます.( 0 )例: EMPテーブルの作成
最古のサンプルテーブルを作成します.
CREATE TABLE emp (empno, ename, job, mgr, hiredate, sal, comm, deptno, email, other_info) as values
(7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, NULL, 20,'[email protected]', '{"skills":["accounting"]}'),
(7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30,'[email protected]', null),
(7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30,'[email protected]', null),
(7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, NULL, 20,'[email protected]', null),
(7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30,'[email protected]', null),
(7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, NULL, 30,'[email protected]', null),
(7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, NULL, 10,'[email protected]', '{"skills":["C","C++","SQL"]}'),
(7788, 'SCOTT', 'ANALYST', 7566, '1982-12-09', 3000, NULL, 20,'[email protected]', '{"cat":"tiger"}'),
(7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000, NULL, 10,'[email protected]', null),
(7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500, 0, 30,'[email protected]', null),
(7876, 'ADAMS', 'CLERK', 7788, '1983-01-12', 1100, NULL, 20,'[email protected]', null),
(7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, NULL, 30,'[email protected]', null),
(7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, NULL, 20,'[email protected]', '{"skills":["SQL","CQL"]}'),
(7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, NULL, 10,'[email protected]', null)
;
mgrからempnoへの自己参照は以下のように宣言できます:ALTER TABLE emp ADD PRIMARY KEY (empno)
;
ALTER TABLE emp ADD FOREIGN KEY (mgr) REFERENCES emp (empno)
;
これは必要ではありませんが、一貫性テストではなく、ビジネスロジックに集中できるようにクエリの正当性を保証します.( 1 )ルートを見つける(レベル0 )
最初のステップはグラフのルートを識別することです.あなたは従業員によって開始し、自分のマネージャーを表示したり、マンガで起動し、その従業員を表示しますか?私は後者をします.階層の根元は管理者のないものです
where mgr is null
)-- (1) query to find the root
select emp.empno,emp.ename,emp.mgr
from emp where mgr is null
;
結果は単純です.yugabyte-> ;
empno | ename | mgr
-------+-------+-----
7839 | KING |
(1 row)
yugabyte=>
(2)このレベル0をwith句で定義する
CTE ( common table expression )に入れます
level0
私はそれから選択します.-- (2) define the root as as level 0 in a CTE
with recursive
level0 as (
-- (1) query to find the root
select emp.empno,emp.ename,emp.mgr
from emp where mgr is null
)
-- (2) query this level
select * from level0
;
結果は同じです.一番上のマネージャーはキングです.yugabyte-> ;
empno | ename | mgr
-------+-------+-----
7839 | KING |
(1 row)
yugabyte=>
私はちょうど1つずつブロックを構築しています.そして、私は前のステップと何が加えられるかについて明確にするために、コメントを残しました.私は既にwith句で再帰的に追加しますが、現時点では、自己参照がないので、1回だけ繰り返します.( 3 )次のレベルへの結合を定義する
私のレベル0が定義されているので
emp
次のレベルを取得するには、次のレベルを取得します.-- (2) define the root as as level 0 in a CTE
with recursive
level0 as (
-- (1) query to find the root
select emp.empno,emp.ename,emp.mgr
from emp where mgr is null
)
-- (3) join employees with level 0
select emp.empno,emp.ename,emp.mgr
,mgr.ename mgr_name
from emp
join level0 mgr on mgr.empno=emp.mgr
;
私はレベル0としてレベルをラベルしているmgr
なぜなら、この新しいレベルの従業員のマネージャーになるからですemp
. 次に、結合mgr.empno=emp.mgr
つのレベルをリンクします.結合の結果ではmgr.ename
ASmgr_name
同じ行のマネージャーの名前を見るために.だからここでは従業員はキングを参照してください:
yugabyte-> ;
empno | ename | mgr | mgr_name
------------+-------+------+----------
7698 | BLAKE | 7839 | KING
7566 | JONES | 7839 | KING
7782 | CLARK | 7839 | KING
(3 rows)
yugabyte=>
( 4 ) 2つのレベルを結合します
マネージャ名を示すのは1ステップでしたが、階層全体を表示するには、すべてのレベルから行を連結する必要があります.私は2つの最初のレベルから始まります.私はすべての組合が必要です.このため、with句で自分のjoinクエリを移動し、
leveln
. 名前が付けられたのにlevel1
何故ならlevel0
しかし、目標は、次のレベルの抽象化することです.私はすべての2つを結合するので、彼らは同じ列が必要です.leveln
マネージャ名をmgr_name
そこで、空の文字列としてクエリを行うときにこれを追加しますlevel0
-- (2) define the root as as level 0 in a CTE
with recursive
level0 as (
-- (1) query to find the root
select emp.empno,emp.ename,emp.mgr
from emp where mgr is null
),
-- (4) put the second level in the WITH clause
leveln as (
-- (3) join employees with level 0
select emp.empno,emp.ename,emp.mgr
,mgr.ename mgr_name
from emp
join level0 mgr on mgr.empno=emp.mgr
)
-- (4) concatenate the two levels
select *,'' mgr_name from level0
union all
select * from leveln
;
これはよく見えるようになります.yugabyte-> ;
empno | ename | mgr | mgr_name
------------+-------+------+----------
7839 | KING | |
7698 | BLAKE | 7839 | KING
7566 | JONES | 7839 | KING
7782 | CLARK | 7839 | KING
(4 rows)
yugabyte=>
これはあなたの結果を検証できる場所です.次のステップは自動的に再帰を加えることです.( 5 ) 1つの再帰式でそれを取得する
私は2つの最初のレベルの結果を検証したので、それを再帰的に取得する時間です.加わる代わりに
level0
私が加わるleveln
これは以前の結果です.そして、私はこのすべてに組合を動かしますleveln
CTE-- (2) define the root as as level 0 in a CTE
with recursive
level0 as (
-- (1) query to find the root
select emp.empno,emp.ename,emp.mgr
from emp where mgr is null
),
-- (4) put the second level in the WITH clause
leveln as (
-- (5) put the UNION ALL in the recursive level
-- (4) concatenate the two levels
select *,'' mgr_name from level0
union all
-- (3) join employees with level 0
select emp.empno,emp.ename,emp.mgr
,mgr.ename mgr_name
from emp
-- (5) change level0 to leveln to join iteratively
join leveln mgr on mgr.empno=emp.mgr
)
-- (5) query this level n
select * from leveln
;
最初の2つのレベルで結合を検証した場合、この最後の変換は自動的です.実行時には、最初の反復は最初のレベルを取得し、unionの2番目の部分はすべて前の結果との結合のために行を持たない.次の繰り返しで結合の結果が追加されます.そして、私たちが葉っぱにいるので、joinが行を返さないまで、前の結果が管理者のリストとみなされて、従業員はいません.yugabyte-> ;
empno | ename | mgr | mgr_name
------------+--------+------+----------
7839 | KING | |
7698 | BLAKE | 7839 | KING
7566 | JONES | 7839 | KING
7782 | CLARK | 7839 | KING
7788 | SCOTT | 7566 | JONES
7902 | FORD | 7566 | JONES
7844 | TURNER | 7698 | BLAKE
7499 | ALLEN | 7698 | BLAKE
7521 | WARD | 7698 | BLAKE
7654 | MARTIN | 7698 | BLAKE
7900 | JAMES | 7698 | BLAKE
7934 | MILLER | 7782 | CLARK
7876 | ADAMS | 7788 | SCOTT
7369 | SMITH | 7902 | FORD
(14 rows)
yugabyte=>
これは魔法がどこにあるのか.慎重にあなたの最初の2つのレベルをテストする場合は、すべての他のクエリは、その前の結果に参加することによって反復的な反復を変換することによって来る.私が最初のレベルを残したことに注意してくださいlevel0
CTEあなたはそれを置くことができますleveln
しかし、私はそれがこれのように読みやすいと思います.実行計画では、それを再帰的な組合として見る
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------
CTE Scan on leveln (cost=20.23..22.86 rows=131 width=72) (actual time=0.023..0.281 rows=14 loops=1)
CTE leveln
-> Recursive Union (cost=0.00..20.23 rows=131 width=46) (actual time=0.021..0.269 rows=14 loops=1)
-> Seq Scan on emp (cost=0.00..1.14 rows=1 width=46) (actual time=0.019..0.021 rows=1 loops=1)
Filter: (mgr IS NULL)
-> Hash Join (cost=0.33..1.65 rows=13 width=46) (actual time=0.054..0.058 rows=3 loops=4)
Hash Cond: (emp_1.mgr = mgr.empno)
-> Seq Scan on emp emp_1 (cost=0.00..1.14 rows=14 width=14) (actual time=0.002..0.003 rows=14 loops=4)
-> Hash (cost=0.20..0.20 rows=10 width=36) (actual time=0.044..0.044 rows=4 loops=4)
-> WorkTable Scan on leveln mgr (cost=0.00..0.20 rows=10 width=36) (actual time=0.001..0.001 rows=4 loops=4)
数loops
番目の部分が実行された方法を多くの時間を指示します.これはPostgreSQLとyugabytedbでも同じです.Reference
この問題について(5段階でSQL再帰的CTEを書く方法を学んでください), 我々は、より多くの情報をここで見つけました https://dev.to/yugabyte/learn-how-to-write-sql-recursive-cte-in-5-steps-3n88テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol