比较同一表中的两组数据-oracle
假设我有一个包含以下数据的表比较同一表中的两组数据-oracle,oracle,dataset,comparison,union,Oracle,Dataset,Comparison,Union,假设我有一个包含以下数据的表 +-------------------------------+ | 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
+-------------------------------+
| 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 |
+--------------------------------------------------------------------------+
执行上述操作的最佳方法是什么?我认为您的示例数据集不正确
对于ID=2d,不应该用分号而不是逗号与R分隔吗?
对于ID=35,不应该用分号而不是冒号与A分隔吗?
我修复了它是否应该被修复,并编写了以下PL/SQL代码;我不知道在纯SQL中是否可以做到这一点。看一看,看它是否有用
它有什么作用?使用嵌套循环,将所有数据集拆分为行,并使用集合运算符INTERSECT,减号决定结果属于哪个组(匹配/仅在基中/仅在目标中)
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>
一个查询中的替代解决方案:
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;
Subbquery q使用从SO到的拆分技术之一。然后,我加入了数据,并计算了匹配/不匹配的单词。只因为函数listag不尊重distinct子句,所以需要子查询a1-a3
此解决方案比较1和4以及4和1。通过将t1.id t2.id和q1.id q2.id中的数据分隔符替换为我修复的示例数据分隔符,您可以将其更改为仅显示一次结果-抱歉!我修复了示例数据分隔符,感谢您指出-现在请通读您的答案!这很有效。我想知道是否有一种更快的方法来实现正则表达式,因为它的伸缩性似乎不太好……好吧,对于这种方法集,我的意思是,你必须将字符串拆分成行。我想任何其他方法都可能更慢。如果可能的话,修改您的数据模型,使其规范化为当前的数据模型-您不应该在同一列中存储不同的信息。因此,我应该先创建一个过程来拆分数据…然后尝试上述操作…抱歉,我不是pl/sql专家,我也不是开发人员..嗯,对。。。如果您是一名开发人员,这会更容易:当我说数据模型不正确时,这意味着您应该创建更多的表,删除或重新创建当前的表。我想你不会那么做的。只有一次,是的-您可能会编写一些代码来拆分数据并将结果存储到其他表中,但是-如果要多次使用这样的代码,这也不是一个好的解决方案。不幸的是,你在为别人的错误买单。
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