Oracle:分层查询中的编号组
Oracle: Numbered groups in a hierarchical query
在我的 Oracle 数据库中,我有一个 table 定义了 predecessors/successors 的层次结构,它既可以分支也可以循环。我附上了 SQL fiddle 来演示 table 是如何工作的。我的意图是为每棵孤立的树分配它自己的编号。请参阅下图以了解所需结果(请注意,在此图片中,成员被命名为 a,b,c,d... 而在所附的 fiddle 中,它们编号为 1,2,3,4...):
http://sqlfiddle.com/#!4/4c887d/4/0
我还没有想出如何构建这样的查询,此刻我非常绝望。
任何帮助,甚至指向解决方案的指针 - 任何类型的输入 - 都将不胜感激。
提前谢谢大家。
换句话说,我的理解是,对于每个 root_node,您需要为其分配一个组号。要找到根节点,我们可以使用 connect_by_root
然后我们可以使用 dense_rank 给它一个数字。
到目前为止我得到了这个,你能检查一下吗sqlfiddle
SELECT predecessor
,successor
,sys_connect_by_path(successor
,'/') AS hierarchy_path
,LEVEL node_level
,dense_rank() over(ORDER BY connect_by_root successor) group_number
FROM tbl_tst
CONNECT BY nocycle PRIOR predecessor = successor;
免责声明:此解决方案适用于 PostgreSQL,不适用于 Oracle。
下面的查询演示了解决为每个子图分配唯一序列号问题的策略。
注意:我尝试为 Oracle 编写查询,但 XMLTable() 函数似乎有问题。如果您找到解决方法(我放弃了),也许您可以将其改编为 Oracle。
with recursive
n (root_id, pred, succ, s) as (
select pred, pred, succ, '/' || least(pred, succ) || '/' || greatest(pred, succ) || '/' from t
union all
select n.root_id, t.pred, t.succ, (
select '/' || string_agg(vl, '/') || '/' from (
select unnest(string_to_array(substring(n.s from 2 for length(n.s) - 2), '/')) as vl
union all select (case when t.pred = n.pred or t.pred = n.succ then t.succ else t.pred end)
order by vl
) y
)
from n
join t on not (n.pred = t.pred and n.succ = t.succ) and (n.pred = t.pred or n.pred = t.succ or n.succ = t.pred or n.succ = t.succ)
and not n.s ~ ('/' || case when t.pred = n.pred or t.pred = n.succ then t.succ else t.pred end || '/')
),
s as (
select distinct s from (
select distinct on (root_id) root_id, s from n group by root_id, s order by root_id, length(s) desc
) j
)
select * from (
select
unnest(string_to_array(substring(s from 2 for length(s) - 2), '/')) as node,
row_number() over(order by s) as g
from s order by s
) z
order by g, node;
结果:
node g
---- -
a 1
b 1
c 1
d 1
e 1
f 2
g 2
h 2
i 3
j 3
请参阅 DB Fiddle 中的 运行 示例。
那些不是层次结构;它们是有向图。您仍然可以在 Oracle 中使用 CONNECT BY
来处理它们,但是没有 START WITH
的根节点,如果您的数据集很大,性能可能会出现问题。
无论如何,你需要做的是 CONNECT BY NOCYCLE
没有 START WITH
。这将从每个单独的节点开始计算一棵树。然后,为每个节点获取 CONNECT_BY_ROOT
并为每个不同的节点值获取 MIN()
。
这是一个带有数据的工作示例。它比它需要的更复杂,因为没有地方可以获得所有节点的完整列表,所以你必须将每个顶点分成两行(一个有 from_node 一个有 to_node ) 以确保您包括所有这些。
CREATE TABLE tbl_tst ( from_node VARCHAR2(1), to_node VARCHAR2(1) );
INSERT INTO tbl_tst VALUES ( 'a', 'b');
INSERT INTO tbl_tst VALUES ( 'b', 'c');
INSERT INTO tbl_tst VALUES ( 'b', 'd');
INSERT INTO tbl_tst VALUES ( 'd', 'a');
INSERT INTO tbl_tst VALUES ( 'd', 'e');
INSERT INTO tbl_tst VALUES ( 'f', 'g');
INSERT INTO tbl_tst VALUES ( 'g', 'h');
INSERT INTO tbl_tst VALUES ( 'h', 'f');
INSERT INTO tbl_tst VALUES ( 'i', 'j');
COMMIT;
WITH groups AS (
SELECT DISTINCT
tt.from_node,
tt.to_node,
MIN(CONNECT_BY_ROOT(from_node))
OVER ( PARTITION BY tt.from_node) group_min
FROM tbl_tst tt
CONNECT BY NOCYCLE from_node = PRIOR to_node
OR to_node = PRIOR from_node
)
SELECT DENSE_RANK() OVER ( ORDER BY group_min ) group_number,
DECODE(splitter.rn,1,groups.from_node,2,groups.to_node) node
FROM groups
CROSS JOIN ( SELECT rownum rn FROM dual CONNECT BY rownum <= 2 ) splitter
GROUP BY DECODE(splitter.rn,1,groups.from_node,2,groups.to_node),
group_min
ORDER BY group_min, node;
+--------------+------+
| GROUP_NUMBER | NODE |
+--------------+------+
| 1 | a |
| 1 | b |
| 1 | c |
| 1 | d |
| 1 | e |
| 2 | f |
| 2 | g |
| 2 | h |
| 3 | i |
| 3 | j |
+--------------+------+
在我的 Oracle 数据库中,我有一个 table 定义了 predecessors/successors 的层次结构,它既可以分支也可以循环。我附上了 SQL fiddle 来演示 table 是如何工作的。我的意图是为每棵孤立的树分配它自己的编号。请参阅下图以了解所需结果(请注意,在此图片中,成员被命名为 a,b,c,d... 而在所附的 fiddle 中,它们编号为 1,2,3,4...):
http://sqlfiddle.com/#!4/4c887d/4/0
我还没有想出如何构建这样的查询,此刻我非常绝望。 任何帮助,甚至指向解决方案的指针 - 任何类型的输入 - 都将不胜感激。
提前谢谢大家。
换句话说,我的理解是,对于每个 root_node,您需要为其分配一个组号。要找到根节点,我们可以使用 connect_by_root
然后我们可以使用 dense_rank 给它一个数字。
到目前为止我得到了这个,你能检查一下吗sqlfiddle
SELECT predecessor
,successor
,sys_connect_by_path(successor
,'/') AS hierarchy_path
,LEVEL node_level
,dense_rank() over(ORDER BY connect_by_root successor) group_number
FROM tbl_tst
CONNECT BY nocycle PRIOR predecessor = successor;
免责声明:此解决方案适用于 PostgreSQL,不适用于 Oracle。
下面的查询演示了解决为每个子图分配唯一序列号问题的策略。
注意:我尝试为 Oracle 编写查询,但 XMLTable() 函数似乎有问题。如果您找到解决方法(我放弃了),也许您可以将其改编为 Oracle。
with recursive
n (root_id, pred, succ, s) as (
select pred, pred, succ, '/' || least(pred, succ) || '/' || greatest(pred, succ) || '/' from t
union all
select n.root_id, t.pred, t.succ, (
select '/' || string_agg(vl, '/') || '/' from (
select unnest(string_to_array(substring(n.s from 2 for length(n.s) - 2), '/')) as vl
union all select (case when t.pred = n.pred or t.pred = n.succ then t.succ else t.pred end)
order by vl
) y
)
from n
join t on not (n.pred = t.pred and n.succ = t.succ) and (n.pred = t.pred or n.pred = t.succ or n.succ = t.pred or n.succ = t.succ)
and not n.s ~ ('/' || case when t.pred = n.pred or t.pred = n.succ then t.succ else t.pred end || '/')
),
s as (
select distinct s from (
select distinct on (root_id) root_id, s from n group by root_id, s order by root_id, length(s) desc
) j
)
select * from (
select
unnest(string_to_array(substring(s from 2 for length(s) - 2), '/')) as node,
row_number() over(order by s) as g
from s order by s
) z
order by g, node;
结果:
node g
---- -
a 1
b 1
c 1
d 1
e 1
f 2
g 2
h 2
i 3
j 3
请参阅 DB Fiddle 中的 运行 示例。
那些不是层次结构;它们是有向图。您仍然可以在 Oracle 中使用 CONNECT BY
来处理它们,但是没有 START WITH
的根节点,如果您的数据集很大,性能可能会出现问题。
无论如何,你需要做的是 CONNECT BY NOCYCLE
没有 START WITH
。这将从每个单独的节点开始计算一棵树。然后,为每个节点获取 CONNECT_BY_ROOT
并为每个不同的节点值获取 MIN()
。
这是一个带有数据的工作示例。它比它需要的更复杂,因为没有地方可以获得所有节点的完整列表,所以你必须将每个顶点分成两行(一个有 from_node 一个有 to_node ) 以确保您包括所有这些。
CREATE TABLE tbl_tst ( from_node VARCHAR2(1), to_node VARCHAR2(1) );
INSERT INTO tbl_tst VALUES ( 'a', 'b');
INSERT INTO tbl_tst VALUES ( 'b', 'c');
INSERT INTO tbl_tst VALUES ( 'b', 'd');
INSERT INTO tbl_tst VALUES ( 'd', 'a');
INSERT INTO tbl_tst VALUES ( 'd', 'e');
INSERT INTO tbl_tst VALUES ( 'f', 'g');
INSERT INTO tbl_tst VALUES ( 'g', 'h');
INSERT INTO tbl_tst VALUES ( 'h', 'f');
INSERT INTO tbl_tst VALUES ( 'i', 'j');
COMMIT;
WITH groups AS (
SELECT DISTINCT
tt.from_node,
tt.to_node,
MIN(CONNECT_BY_ROOT(from_node))
OVER ( PARTITION BY tt.from_node) group_min
FROM tbl_tst tt
CONNECT BY NOCYCLE from_node = PRIOR to_node
OR to_node = PRIOR from_node
)
SELECT DENSE_RANK() OVER ( ORDER BY group_min ) group_number,
DECODE(splitter.rn,1,groups.from_node,2,groups.to_node) node
FROM groups
CROSS JOIN ( SELECT rownum rn FROM dual CONNECT BY rownum <= 2 ) splitter
GROUP BY DECODE(splitter.rn,1,groups.from_node,2,groups.to_node),
group_min
ORDER BY group_min, node;
+--------------+------+ | GROUP_NUMBER | NODE | +--------------+------+ | 1 | a | | 1 | b | | 1 | c | | 1 | d | | 1 | e | | 2 | f | | 2 | g | | 2 | h | | 3 | i | | 3 | j | +--------------+------+