我正在研究Oracle中的一个问题,我正在努力解决'优雅'问题。
我有一个带有三个不同标识符的数据提取:A,B,C
每个标识符可以出现在多个行中,并且每行可以具有这三个标识符中的一个或多个(即列已填充或为空)。
我希望能够将所有共有A,B或C组合的记录分组,并为它们分配相同的组ID。
提取表格,显示最终的组应该是什么:
Rownum | A | B | C | End group
1 p NULL NULL 1
2 p r NULL 1
3 q NULL NULL 2
4 NULL r NULL 1
5 NULL NULL s 2
6 q NULL s 2
我最初的方法是为摘录中的每一行分配一个guid,并为三个标识符创建一个查找表:
GUID | IDENTIFIER | IDENTIFIER TYPE | GROUP | END GROUP
1 p A 1 1
2 p A 1 1
2 r B 2 1
3 q A 3 3
4 r B 2 1
5 s C 4 3
6 q A 3 3
6 s C 4 3
然后按标识符分组并分配组号。但是,这些组需要尽可能组合以提供端组中显示的视图。
我能想到的唯一解决这个问题的方法是使用循环,我宁愿避免使用。
非常感谢任何想法。
尼尔
答案 0 :(得分:4)
这确实是一个有趣的问题。不过,我认为我们错过了“群体”的定义。由于在您的示例中(p,null,null)
(row1)和(null,r,null)
(row4)共享没有共同的标识符,并且属于我将使用的同一组这个分组定义:
如果行与该组中至少有一行共享至少一个标识符,则该行属于某个组。
这意味着我们可以“链接”行。这自然会导致分层解决方案:
SQL> SELECT ID, a, b, c, MIN(grp) grp
2 FROM (SELECT connect_by_root(id) ID,
3 connect_by_root(a) a,
4 connect_by_root(b) b,
5 connect_by_root(c) c,
6 ID grp
7 FROM a
8 CONNECT BY NOCYCLE(PRIOR a = a
9 OR PRIOR b = b
10 OR PRIOR c = c))
11 GROUP BY ID, a, b, c
12 ORDER BY ID;
ID A B C GRP
---------- ---------- ---------- ---------- ----------
1 p 1
2 p r 1
3 q 3
4 r 1
5 s 3
6 q s 3
6 rows selected
您可以执行子查询来理解构造:
SQL> SELECT connect_by_root(id) ID,
2 connect_by_root(a) a,
3 connect_by_root(b) b,
4 connect_by_root(c) c,
5 substr(sys_connect_by_path(ID, '->'), 3) path,
6 ID grp
7 FROM a
8 CONNECT BY NOCYCLE(a = PRIOR a
9 OR b = PRIOR b
10 OR c = PRIOR c);
ID A B C PATH GRP
---------- ---------- ---------- ---------- -------- ----------
1 p 1 1
1 p 1->2 2
1 p 1->2->4 4
2 p r 2 2
2 p r 2->1 1
2 p r 2->4 4
3 q 3 3
3 q 3->6 6
3 q 3->6->5 5
4 r 4 4
4 r 4->2 2
4 r 4->2->1 1
5 s 5 5
5 s 5->6 6
5 s 5->6->3 3
6 q s 6 6
6 q s 6->3 3
6 q s 6->5 5
18 rows selected
答案 1 :(得分:2)
使用merge而不是loop:
Table a(a,b,c,groupId)
说明:
merge into a
USING (SELECT RANK() OVER(ORDER BY a,b,c) g, ROWID rid FROM a) SOURCE
ON (a.ROWID = SOURCE.rid)
WHEN MATCHED THEN UPDATE SET a.GroupId = SOURCE.g
与:
相同 BEGIN
FOR x IN ( SELECT RANK() OVER(ORDER BY a,b,c) g, ROWID rid FROM a)
LOOP
UPDATE a
SET GroupId = x.g
WHERE a.RowId = x.rid;
END LOOP;
END;