Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit aaa0cc4

Browse files
committed
Update to sort random order cause of optimizer
Capture PK Hash to improve lookup
1 parent 8133f11 commit aaa0cc4

8 files changed

Lines changed: 134 additions & 66 deletions

File tree

docs/userguide/advanced_data_comparison.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ Above test will result in two differences of one row extra and one row missing.
137137

138138

139139

140-
## Join by option
140+
## Join By option
141141

142142
You can now join two cursors by defining a primary key or composite key that will be used to uniquely identify and compare rows. This option allows us to exactly show which rows are missing, extra and which are different without ordering clause.
143143

@@ -157,7 +157,15 @@ begin
157157
ut.expect( l_actual ).to_equal( l_expected ).join_by('USERNAME');
158158
end;
159159
```
160-
This will show you diffrence in row 'TEST' regardless of order.
160+
This will show you difference in row 'TEST' regardless of order.
161+
162+
```sql
163+
Rows: [ 1 differences ]
164+
Expected: <USER_ID>-600</USER_ID> for key: TEST
165+
Actual: <USER_ID>-610</USER_ID> for key: TEST
166+
```
167+
168+
**Please note that .join_by option will take longer to process due to need of parsing via primary keys.**
161169

162170
## Defining item as XPath
163171
When using XPath expression, keep in mind the following:

source/expectations/data_values/ut_compound_data_diff_tmp.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ create global temporary table ut_compound_data_diff_tmp(
1515
diff_id raw(128),
1616
item_no integer,
1717
pk_hash raw(128),
18-
item_hash raw(128),
18+
pk_value varchar2(4000),
19+
item_hash raw(128),
1920
duplicate_no integer,
2021
constraint ut_compound_data_diff_tmp_uk1 unique (diff_id,duplicate_no,item_no,item_hash, pk_hash),
2122
constraint ut_compound_data_diff_tmp_chk check(

source/expectations/data_values/ut_compound_data_helper.pkb

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,15 @@ create or replace package body ut_compound_data_helper is
176176
**/
177177

178178
execute immediate q'[
179-
with diff_info as (select item_hash,pk_hash from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid)
180-
select rn,diff_type,diffed_row from
179+
with diff_info as (select item_hash,pk_hash,pk_value from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid)
180+
select rn,diff_type,diffed_row,pk_value from
181181
(
182-
select diff_type,diffed_row, dense_rank() over (order by pk_hash) rn from
182+
select diff_type,diffed_row, dense_rank() over (order by pk_hash) rn,pk_value from
183183
(
184-
select diff_type,diffed_row,pk_hash from
185-
(select diff_type,data_item diffed_row,pk_hash
184+
select diff_type,diffed_row,pk_hash,pk_value from
185+
(select diff_type,data_item diffed_row,pk_hash,pk_value
186186
from
187-
(select nvl(exp.pk_hash, act.pk_hash) pk_hash,
187+
(select nvl(exp.pk_hash, act.pk_hash) pk_hash,nvl(exp.pk_value, act.pk_value) pk_value,
188188
xmlserialize(content exp.row_data no indent) exp_item,
189189
xmlserialize(content act.row_data no indent) act_item
190190
from
@@ -193,10 +193,11 @@ create or replace package body ut_compound_data_helper is
193193
(select ucd.column_value row_data,
194194
r.item_hash row_hash,
195195
r.pk_hash ,
196+
r.pk_value,
196197
ucd.column_value.getRootElement() col_name,
197198
ucd.column_value.getclobval() col_val
198199
from
199-
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_hash, i.pk_hash
200+
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_hash, i.pk_hash, i.pk_value
200201
from ut_compound_data_tmp ucd,
201202
diff_info i
202203
where ucd.data_id = :self_guid
@@ -211,10 +212,11 @@ create or replace package body ut_compound_data_helper is
211212
(select ucd.column_value row_data,
212213
r.item_hash row_hash,
213214
r.pk_hash ,
215+
r.pk_value,
214216
ucd.column_value.getRootElement() col_name,
215217
ucd.column_value.getclobval() col_val
216218
from
217-
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_hash, i.pk_hash
219+
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_hash, i.pk_hash, i.pk_value
218220
from ut_compound_data_tmp ucd,
219221
diff_info i
220222
where ucd.data_id = :other_guid
@@ -231,7 +233,8 @@ create or replace package body ut_compound_data_helper is
231233
union all
232234
select case when exp.pk_hash is null then 'Extra:' else 'Missing:' end as diff_type,
233235
xmlserialize(content nvl(exp.item_data, act.item_data) no indent) diffed_row,
234-
coalesce(exp.pk_hash,act.pk_hash) pk_hash
236+
coalesce(exp.pk_hash,act.pk_hash) pk_hash,
237+
null pk_value
235238
from (select extract(ucd.item_data,'/*/*') item_data,i.pk_hash
236239
from ut_compound_data_tmp ucd,
237240
diff_info i
@@ -269,9 +272,12 @@ create or replace package body ut_compound_data_helper is
269272
l_column_filter := get_columns_filter(a_exclude_xpath,a_include_xpath);
270273
execute immediate q'[
271274
with
272-
diff_info as (select item_no from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid and rownum <= :max_rows)
275+
diff_info as ( select item_no
276+
from
277+
(select item_no from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid order by item_no asc)
278+
where rownum <= :max_rows)
273279
select *
274-
from (select rn, diff_type, xmlserialize(content data_item no indent) diffed_row
280+
from (select rn, diff_type, xmlserialize(content data_item no indent) diffed_row, null pk_value
275281
from (select nvl(exp.rn, act.rn) rn,
276282
xmlagg(exp.col order by exp.col_no) exp_item,
277283
xmlagg(act.col order by act.col_no) act_item
@@ -307,7 +313,8 @@ create or replace package body ut_compound_data_helper is
307313
union all
308314
select nvl(exp.item_no, act.item_no) rn,
309315
case when exp.item_no is null then 'Extra:' else 'Missing:' end as diff_type,
310-
xmlserialize(content nvl(exp.item_data, act.item_data) no indent) diffed_row
316+
xmlserialize(content nvl(exp.item_data, act.item_data) no indent) diffed_row,
317+
null pk_value
311318
from (select ucd.item_no, extract(ucd.item_data,'/*/*') item_data
312319
from ut_compound_data_tmp ucd
313320
where ucd.data_id = :self_guid
@@ -348,7 +355,8 @@ create or replace package body ut_compound_data_helper is
348355
diff_info as (select item_hash,duplicate_no from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid)
349356
select duplicate_no,
350357
diffed_type,
351-
diffed_row
358+
diffed_row,
359+
null pk_value
352360
from
353361
(select
354362
coalesce(exp.duplicate_no,act.duplicate_no) duplicate_no,

source/expectations/data_values/ut_compound_data_helper.pks

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ create or replace package ut_compound_data_helper authid definer is
3131
type t_row_diffs is record(
3232
rn integer,
3333
diff_type varchar2(250),
34-
diffed_row clob
34+
diffed_row clob,
35+
pk_value varchar2(4000)
3536
);
3637

3738
type tt_row_diffs is table of t_row_diffs;

source/expectations/data_values/ut_compound_data_tmp.sql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ create global temporary table ut_compound_data_tmp(
1616
item_no integer,
1717
item_data xmltype,
1818
item_hash raw(128),
19-
pk_hash raw(128),
2019
duplicate_no integer,
2120
constraint ut_cmp_data_tmp_hash_pk unique (data_id,item_no, item_hash , duplicate_no)
2221
) on commit preserve rows;

source/expectations/data_values/ut_compound_data_value.tpb

Lines changed: 82 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ create or replace type body ut_compound_data_value as
139139
l_actual ut_compound_data_value;
140140
l_diff_id ut_compound_data_helper.t_hash;
141141
l_row_diffs ut_compound_data_helper.tt_row_diffs;
142+
142143
begin
143144
if not a_other is of (ut_compound_data_value) then
144145
raise value_error;
@@ -173,7 +174,11 @@ create or replace type body ut_compound_data_value as
173174
ut_utils.append_to_clob( l_result, l_message );
174175
for i in 1 .. l_row_diffs.count loop
175176
l_results.extend;
176-
l_results(l_results.last) := ' Row No. '||l_row_diffs(i).rn||' - '||rpad(l_row_diffs(i).diff_type,10)||l_row_diffs(i).diffed_row;
177+
if a_join_by_xpath is not null and l_row_diffs(i).pk_value is not null then
178+
l_results(l_results.last) := ' '||rpad(l_row_diffs(i).diff_type,10)||l_row_diffs(i).diffed_row||' for key: '||l_row_diffs(i).pk_value;
179+
else
180+
l_results(l_results.last) := ' Row No. '||l_row_diffs(i).rn||' - '||rpad(l_row_diffs(i).diff_type,10)||l_row_diffs(i).diffed_row;
181+
end if;
177182
end loop;
178183
ut_utils.append_to_clob(l_result,l_results);
179184
end if;
@@ -245,16 +250,28 @@ create or replace type body ut_compound_data_value as
245250
l_row_diffs ut_compound_data_helper.tt_row_diffs;
246251
c_max_rows constant integer := 20;
247252

248-
type t_fetch_pk_hash_rec is record
253+
type t_pk_val_rec is record
249254
(
250-
data_id raw(32),
251-
item_hash raw(128),
252-
item_data xmltype
255+
data_id raw(32),
256+
item_hash raw(128),
257+
pk_hash raw(128),
258+
pk_value varchar2(4000)
253259
);
254-
type t_fetch_pk_hash_tab is table of t_fetch_pk_hash_rec;
255260

256-
l_fetch_tab t_fetch_pk_hash_tab;
257-
l_pk_hash_cursor sys_refcursor;
261+
type t_pk_val_tab is table of t_pk_val_rec;
262+
l_pk_val_tab t_pk_val_tab;
263+
264+
function get_column_xpath(a_join_by_xpath varchar2) return varchar2 is
265+
l_column varchar2(32767);
266+
begin
267+
if a_join_by_xpath is not null then
268+
l_column := l_ut_owner ||'.ut_compound_data_helper.get_hash(extract(t.item_data,:join_by_xpath).GetClobVal()) pk_hash ';
269+
else
270+
l_column := ':join_by_xpath pk_hash ';
271+
end if;
272+
return l_column;
273+
end;
274+
258275
begin
259276
if not a_other is of (ut_compound_data_value) then
260277
raise value_error;
@@ -268,60 +285,78 @@ create or replace type body ut_compound_data_value as
268285
/**
269286
* Due to incompatibility issues in XML between 11 and 12.2 and 12.1 versions we will prepopulate pk_hash upfront to
270287
* avoid optimizer incorrectly rewrite and causing NULL error or ORA-600
271-
**/
288+
**/
289+
-- Pre generate hash minus to leave only onese that are diffrent, for example duplicates or diffrent hash
290+
/*if a_join_by_xpath is not null then
291+
execute immediate q'[select
292+
data_id,item_hash,pk_hash,
293+
listagg(extractvalue(tcd.column_value, '/*'), '; ') within GROUP(ORDER BY item_hash) pk_value
294+
from
295+
(select
296+
ucd.column_value row_data,
297+
]'|| l_ut_owner ||q'[.ut_compound_data_helper.get_hash(extract(ucd.column_value,:join_xpath).GetClobVal()) pk_hash ,
298+
t.item_hash,
299+
t.data_id
300+
from ]' || l_ut_owner || q'[.ut_compound_data_tmp t ,
301+
table(xmlsequence(extract(t.item_data,'/*'))) ucd
302+
where data_id = :self_guid or data_id = :other_guid ),
303+
table(xmlsequence(extract(row_data ,:join_xpath))) tcd
304+
group by pk_hash,item_hash,data_id]'
305+
bulk collect into l_pk_val_tab using a_join_by_xpath,self.data_id, l_other.data_id,a_join_by_xpath;
272306

273-
if a_join_by_xpath is not null then
274-
275-
open l_pk_hash_cursor for q'[select t.data_id,t.item_hash,t.item_data
276-
from ]'|| l_ut_owner ||q'[.ut_compound_data_tmp t
277-
,xmltable('*'
278-
passing t.item_data
279-
columns
280-
xml_pk_hash clob path ']'|| a_join_by_xpath ||q'['
281-
) ucd
282-
where data_id = :self_guid or data_id = :other_guid ]'
283-
using self.data_id, l_other.data_id;
284-
fetch l_pk_hash_cursor bulk collect into l_fetch_tab;
285-
for pks in 1..l_fetch_tab.COUNT
286-
loop
287-
execute immediate 'update '|| l_ut_owner ||'.ut_compound_data_tmp
288-
set pk_hash = '|| l_ut_owner ||'.ut_compound_data_helper.get_hash(extract(:item_data,:join_by_xpath).GetClobVal())
289-
where item_hash = :item_hash'
290-
using l_fetch_tab(pks).item_data,a_join_by_xpath , l_fetch_tab(pks).item_hash;
291-
end loop;
292-
293-
close l_pk_hash_cursor;
307+
forall pk_vals in 1..l_pk_val_tab.COUNT
308+
update ut_compound_data_tmp
309+
set pk_hash = l_pk_val_tab(pk_vals).pk_hash
310+
,pk_value = l_pk_val_tab(pk_vals).pk_value
311+
where data_id = l_pk_val_tab(pk_vals).data_id
312+
and item_hash = l_pk_val_tab(pk_vals).item_hash;
294313
end if;
295-
296-
297-
-- Pre generate hash minus to leave only onese that are diffrent, for example duplicates or diffrent hash
298-
execute immediate 'insert into ' || l_ut_owner || '.ut_compound_data_diff_tmp ( diff_id,item_hash,pk_hash,duplicate_no )
314+
*/
315+
316+
execute immediate 'insert into ' || l_ut_owner || '.ut_compound_data_diff_tmp ( diff_id,item_hash,pk_hash,duplicate_no)
317+
with calc_pk as
318+
( select data_id,item_hash, '||get_column_xpath(a_join_by_xpath)||'
319+
from ' || l_ut_owner || '.ut_compound_data_tmp t
320+
)
299321
select distinct :diff_id,tmp.item_hash,tmp.pk_hash,tmp.duplicate_no
300322
from(
301323
(
302324
select t.item_hash,row_number() over (partition by t.item_hash,t.data_id order by 1,2) duplicate_no,
303-
pk_hash
304-
from ' || l_ut_owner || '.ut_compound_data_tmp t
305-
where data_id = :self_guid
325+
pc.pk_hash
326+
from ' || l_ut_owner || '.ut_compound_data_tmp t,
327+
calc_pk pc
328+
where t.data_id = :self_guid
329+
and pc.data_id = t.data_id
330+
and pc.item_hash = t.item_hash
306331
minus
307332
select t.item_hash,row_number() over (partition by t.item_hash,t.data_id order by 1,2) duplicate_no,
308-
pk_hash
309-
from ' || l_ut_owner || '.ut_compound_data_tmp t
310-
where data_id = :other_guid
333+
pc.pk_hash
334+
from ' || l_ut_owner || '.ut_compound_data_tmp t,
335+
calc_pk pc
336+
where t.data_id = :other_guid
337+
and pc.data_id = t.data_id
338+
and pc.item_hash = t.item_hash
311339
)
312340
union all
313341
(
314342
select t.item_hash,row_number() over (partition by t.item_hash,t.data_id order by 1,2) duplicate_no,
315-
pk_hash
316-
from ' || l_ut_owner || '.ut_compound_data_tmp t
317-
where data_id = :other_guid
343+
pc.pk_hash
344+
from ' || l_ut_owner || '.ut_compound_data_tmp t,
345+
calc_pk pc
346+
where t.data_id = :other_guid
347+
and pc.data_id = t.data_id
348+
and pc.item_hash = t.item_hash
318349
minus
319350
select t.item_hash,row_number() over (partition by t.item_hash,t.data_id order by 1,2) duplicate_no,
320-
pk_hash
321-
from ' || l_ut_owner || '.ut_compound_data_tmp t
322-
where data_id = :self_guid
351+
pc.pk_hash
352+
from ' || l_ut_owner || '.ut_compound_data_tmp t,
353+
calc_pk pc
354+
where t.data_id = :self_guid
355+
and pc.data_id = t.data_id
356+
and pc.item_hash = t.item_hash
323357
))tmp'
324-
using l_diff_id,
358+
using a_join_by_xpath,
359+
l_diff_id,
325360
self.data_id, l_other.data_id,
326361
l_other.data_id,self.data_id;
327362
--result is OK only if both are same

test/core/expectations/compound_data/test_expectations_cursor.pkb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,19 @@ Extra: <ROW><USERNAME>test</USERNAME><USER_ID>-666</USER_ID></ROW>%]';
11131113
--Assert
11141114
ut.expect(expectations.failed_expectations_data()).to_be_empty();
11151115
end;
1116-
1116+
1117+
procedure cursor_joinby_compare_1000 is
1118+
l_actual SYS_REFCURSOR;
1119+
l_expected SYS_REFCURSOR;
1120+
begin
1121+
--Arrange
1122+
open l_actual for select object_name from all_objects where rownum <=1100;
1123+
open l_expected for select object_name from all_objects where rownum <=1100;
1124+
--Act
1125+
ut3.ut.expect(l_actual).to_equal(l_expected).join_by('OBJECT_NAME');
1126+
--Assert
1127+
ut.expect(expectations.failed_expectations_data()).to_be_empty();
1128+
end;
1129+
11171130
end;
11181131
/

test/core/expectations/compound_data/test_expectations_cursor.pks

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,8 @@ create or replace package test_expectations_cursor is
211211
--%test( Compare cursors join by composite key)
212212
procedure cursor_joinby_compare_twocols;
213213

214+
--%test( Compare cursors join by single key more than 1000 rows)
215+
procedure cursor_joinby_compare_1000;
216+
214217
end;
215218
/

0 commit comments

Comments
 (0)