假设我有一个包含以下数据的表。
+-------------------------------+
| UniqueID Name Dataset |
+-------------------------------+
| 1 ABC1 A:B;C:D;E:F |
| 2 ABC2 A:B;C:D;R:S |
| 3 ABC3 C:4;G:5;A:B |
| 4 ABC4 A:B;C:D;E:F |
+-------------------------------+
其中数据集是数据的组合,例如A:B,以;分隔
有效地,我想做的是将每组数据与另一条记录进行比较,并通过比较“数据集”来得到如下图所示的图片(这只是第一次比较)
+--------------------------------------------------------------------------+
| UniqueID Name UniqueID Name Matched on OnlyinBase OnlyinTarget |
+--------------------------------------------------------------------------+
| 1 ABC1 2 ABC2 A:B;C:D E:F R:S |
| etc |
+--------------------------------------------------------------------------+
做上述事情的最好方法是什么?
答案 0 :(得分:1)
一个查询中的替代解决方案:
with
-- sample data
t(Id, Name, Dataset) as (
select 1, 'ABC1', 'A:B;C:D;E:F' from dual union all
select 2, 'ABC2', 'A:B;C:D;R:S' from dual union all
select 3, 'ABC3', 'C:4;G:5;A:B' from dual union all
select 4, 'ABC4', 'A:B;C:D;E:F' from dual ),
-- end of sample data
q as (
select distinct id, name,
trim(regexp_substr(t.dataset, '[^;]+', 1, ls.column_value)) as ds
from t, table(cast(multiset(select level from dual
connect by level <= length(regexp_replace(t.dataset, '[^;]+'))+1)
as sys.odcinumberlist)) ls),
p as (select q1.id id1, q1.name name1, q2.id id2, q2.name name2, q1.ds set1, q2.ds set2,
max(case when q1.ds = q2.ds then 1 else 0 end)
over (partition by q1.id, q2.id, q1.ds) m1,
max(case when q1.ds = q2.ds then 1 else 0 end)
over (partition by q1.id, q2.id, q2.ds) m2
from q q1 join q q2 on q1.id <> q2.id),
a1 as (select distinct id1, id2, set1 ds from p where m1 = 0),
a2 as (select distinct id1, id2, set1 ds from p where m1 = 1),
a3 as (select distinct id1, id2, set2 ds from p where m2 = 0)
select t1.id id1, t1.name name1, t2.id id2, t2.name name2,
(select listagg(ds, ' ; ') within group (order by ds)
from a1 where id1 = t1.id and id2 = t2.id) l1,
(select listagg(ds, ' ; ') within group (order by ds)
from a2 where id1 = t1.id and id2 = t2.id) l2,
(select listagg(ds, ' ; ') within group (order by ds)
from a3 where id1 = t1.id and id2 = t2.id) l3
from t t1
join t t2 on t1.id <> t2.id;
结果:
ID1 NAME1 ID2 NAME2 L1 L2 L3
------ ----- ------ ----- ------------ ---------------- -------------
1 ABC1 2 ABC2 E:F A:B ; C:D R:S
1 ABC1 3 ABC3 C:D ; E:F A:B C:4 ; G:5
1 ABC1 4 ABC4 A:B ; C:D ; E:F
2 ABC2 1 ABC1 R:S A:B ; C:D E:F
2 ABC2 3 ABC3 C:D ; R:S A:B C:4 ; G:5
2 ABC2 4 ABC4 R:S A:B ; C:D E:F
3 ABC3 1 ABC1 C:4 ; G:5 A:B C:D ; E:F
3 ABC3 2 ABC2 C:4 ; G:5 A:B C:D ; R:S
3 ABC3 4 ABC4 C:4 ; G:5 A:B C:D ; E:F
4 ABC4 1 ABC1 A:B ; C:D ; E:F
4 ABC4 2 ABC2 E:F A:B ; C:D R:S
4 ABC4 3 ABC3 C:D ; E:F A:B C:4 ; G:5
12 rows selected
子查询q
使用从SO到divide words into separate rows的拆分技术之一。然后,我自加入数据并计算了匹配/不匹配的单词。子查询a1
-a3
仅由于函数listagg
不遵守distinct
子句而需要。
此解决方案比较(1和4)和(4和1)。通过将<>
和t1.id <> t2.id
中的q1.id <> q2.id
替换为<
,可以将其更改为仅显示一次结果。
答案 1 :(得分:0)
我认为您的示例数据集不正确。
ID = 2
D
是否应该用分号(而不是逗号)与R
隔开? ID = 3
5
是否应该用分号(而不是冒号)与A
隔开?我修复了(如果应该修复)并编写了以下PL / SQL代码;我不知道您是否可以在纯SQL中做到这一点。看看是否有帮助。
它有什么作用?使用嵌套循环,将所有DATASET
拆分为行,并且-使用SET
运算符(INTERSECT
,MINUS
)决定结果所属的组(匹配项/仅在基数/中仅限目标)。
SQL> select * from test;
ID NAME DATASET
---------- ---- --------------------
1 ABC1 A:B;C:D;E:F
2 ABC2 A:B;C:D;R:S
3 ABC3 C:4;G:5;A:B
4 ABC4 A:B;C:D;E:F
SQL> set serveroutput on
SQL>
SQL> DECLARE
2 l_matched VARCHAR2 (20);
3 l_base VARCHAR2 (20);
4 l_target VARCHAR2 (20);
5 BEGIN
6 FOR cur_1 IN ( SELECT id, name, dataset
7 FROM test
8 ORDER BY id)
9 LOOP
10 FOR cur_2 IN ( SELECT id, name, dataset
11 FROM test
12 WHERE id > cur_1.id
13 ORDER BY id)
14 LOOP
15 -- Matched
16 SELECT LISTAGG (col, ';') WITHIN GROUP (ORDER BY col)
17 INTO l_matched
18 FROM ( SELECT REGEXP_SUBSTR (cur_1.dataset,
19 '[^;]+',
20 1,
21 LEVEL)
22 col
23 FROM DUAL
24 CONNECT BY LEVEL <= REGEXP_COUNT (cur_1.dataset, ';') + 1
25 INTERSECT
26 SELECT REGEXP_SUBSTR (cur_2.dataset,
27 '[^;]+',
28 1,
29 LEVEL)
30 col
31 FROM DUAL
32 CONNECT BY LEVEL <= REGEXP_COUNT (cur_2.dataset, ';') + 1);
33
34 -- Only in base
35 SELECT LISTAGG (col, ';') WITHIN GROUP (ORDER BY col)
36 INTO l_base
37 FROM ( SELECT REGEXP_SUBSTR (cur_1.dataset,
38 '[^;]+',
39 1,
40 LEVEL)
41 col
42 FROM DUAL
43 CONNECT BY LEVEL <= REGEXP_COUNT (cur_1.dataset, ';') + 1
44 MINUS
45 SELECT REGEXP_SUBSTR (cur_2.dataset,
46 '[^;]+',
47 1,
48 LEVEL)
49 col
50 FROM DUAL
51 CONNECT BY LEVEL <= REGEXP_COUNT (cur_2.dataset, ';') + 1);
52
53 -- Only in target
54 SELECT LISTAGG (col, ';') WITHIN GROUP (ORDER BY col)
55 INTO l_target
56 FROM ( SELECT REGEXP_SUBSTR (cur_2.dataset,
57 '[^;]+',
58 1,
59 LEVEL)
60 col
61 FROM DUAL
62 CONNECT BY LEVEL <= REGEXP_COUNT (cur_2.dataset, ';') + 1
63 MINUS
64 SELECT REGEXP_SUBSTR (cur_1.dataset,
65 '[^;]+',
66 1,
67 LEVEL)
68 col
69 FROM DUAL
70 CONNECT BY LEVEL <= REGEXP_COUNT (cur_1.dataset, ';') + 1);
71
72 DBMS_OUTPUT.put_line (
73 cur_1.id
74 || ' '
75 || cur_1.name
76 || ' '
77 || cur_2.id
78 || ' '
79 || cur_2.name
80 || ' '
81 || rpad(l_matched, 20, ' ')
82 || ' '
83 || rpad(l_base, 20, ' ')
84 || ' '
85 || rpad(l_target, 20, ' '));
86 END LOOP;
87 END LOOP;
88 END;
89 /
1 ABC1 2 ABC2 A:B;C:D E:F R:S
1 ABC1 3 ABC3 A:B C:D;E:F C:4;G:5
1 ABC1 4 ABC4 A:B;C:D;E:F
2 ABC2 3 ABC3 A:B C:D;R:S C:4;G:5
2 ABC2 4 ABC4 A:B;C:D R:S E:F
3 ABC3 4 ABC4 A:B C:4;G:5 C:D;E:F
PL/SQL procedure successfully completed.
SQL>