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

Skip to content

Commit b8840a1

Browse files
committed
Updated join by
updated docs
1 parent 0fc68d1 commit b8840a1

5 files changed

Lines changed: 191 additions & 82 deletions

File tree

docs/userguide/advanced_data_comparison.md

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ utPLSQL expectations incorporates advanced data comparison options when comparin
77
- nested table and varray
88

99
Advanced data-comparison options are available for the [`equal`](expectations.md#equal) matcher.
10-
10+
1111
## Syntax
1212

1313
```
@@ -58,7 +58,7 @@ Columns 'ignore_me' and "ADate" will get excluded from cursor comparison.
5858
The cursor data is equal, when those columns are excluded.
5959

6060
This option is useful in scenarios, when you need to exclude incomparable/unpredictable column data like CREATE_DATE of a record that is maintained by default value on a table column.
61-
61+
6262
## Selecting columns for data comparison
6363

6464
Consider the following example
@@ -95,8 +95,51 @@ Only the columns 'RN', "A_Column" will be compared. Column 'SOME_COL' is exclude
9595

9696
This option can be useful in scenarios where you need to narrow-down the scope of test so that the test is only focused on very specific data.
9797

98-
## Join By option
99-
You can now join two cursors by defining a primary key or composite key that will be used to uniqely identify and compare rows. This option allows us to exactly show which rows are missing, extra and which are diffrent without ordering clause.
98+
##Unordered
99+
100+
Unordered option allows for quick comparison of two cursors without need of ordering them in any way.
101+
102+
Result of such comparison will be limited to only information about row existing or not existing in given set without actual information about exact differences.
103+
104+
105+
106+
```sql
107+
procedure unordered_tst is
108+
l_actual sys_refcursor;
109+
l_expected sys_refcursor;
110+
begin
111+
open l_expected for select username, user_id from all_users
112+
union all
113+
select 'TEST' username, -600 user_id from dual
114+
order by 1 desc;
115+
open l_actual for select username, user_id from all_users
116+
union all
117+
select 'TEST' username, -610 user_id from dual
118+
order by 1 asc;
119+
ut.expect( l_actual ).to_equal( l_expected ).unordered;
120+
end;
121+
```
122+
123+
124+
125+
Above test will result in two differences of one row extra and one row missing.
126+
127+
128+
129+
```sql
130+
Diff:
131+
Rows: [ 2 differences ]
132+
Missing: <ROW><USERNAME>TEST</USERNAME><USER_ID>-600</USER_ID></ROW>
133+
Extra: <ROW><USERNAME>TEST</USERNAME><USER_ID>-610</USER_ID></ROW>
134+
```
135+
136+
137+
138+
139+
140+
## Join by option
141+
142+
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.
100143

101144
```sql
102145
procedure join_by_username is
@@ -133,4 +176,4 @@ begin
133176
open l_actual for select rownum as rn, 'a' as "A_Column", 'x' SOME_COL, a.* from all_objects a where rownum < 4;
134177
ut.expect( l_actual ).to_equal( l_expected ).include( '/ROW/RN|/ROW/A_Column|/ROW/SOME_COL' );
135178
end;
136-
```
179+
```

source/expectations/data_values/ut_compound_data_diff_tmp.sql

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ create global temporary table ut_compound_data_diff_tmp(
1414
*/
1515
diff_id raw(128),
1616
item_no integer,
17-
item_hash raw(128),
17+
pk_hash raw(128),
18+
item_hash raw(128),
1819
duplicate_no integer,
19-
constraint ut_compound_data_diff_tmp_uk1 unique (diff_id, item_no, item_hash, duplicate_no),
20+
constraint ut_compound_data_diff_tmp_uk1 unique (diff_id,duplicate_no,item_no,item_hash, pk_hash),
2021
constraint ut_compound_data_diff_tmp_chk check(
21-
item_no is not null and item_hash is null and duplicate_no is null
22+
item_no is not null and pk_hash is null and duplicate_no is null
2223
or item_no is null and item_hash is not null and duplicate_no is not null
24+
or item_no is null and pk_hash is not null and duplicate_no is not null
2325
)
2426
) on commit preserve rows;

source/expectations/data_values/ut_compound_data_helper.pkb

Lines changed: 74 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -176,72 +176,85 @@ create or replace package body ut_compound_data_helper is
176176
**/
177177

178178
execute immediate q'[
179-
with diff_info as (select item_hash from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid)
180-
select rn,diff_type,diffed_row
181-
from
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
182181
(
183-
select rn,diff_type,diffed_row
184-
from
185-
(select dense_rank() over (order by pk_hash) as rn, diff_type,data_item diffed_row
182+
select diff_type,diffed_row, dense_rank() over (order by pk_hash) rn from
183+
(
184+
select diff_type,diffed_row,pk_hash from
185+
(select diff_type,data_item diffed_row,pk_hash
186186
from
187-
(select pk_hash
188-
,case when exp_item is not null and act_item is not null then exp_item else null end exp_item
189-
,case when exp_item is not null and act_item is not null then act_item else null end act_item
190-
,case when exp_item is not null and act_item is null then exp_item else null end miss_item
191-
,case when exp_item is null and act_item is not null then act_item else null end ext_item
192-
from
193-
(select nvl(exp.pk_hash, act.pk_hash) pk_hash,
187+
(select nvl(exp.pk_hash, act.pk_hash) pk_hash,
194188
xmlserialize(content exp.row_data no indent) exp_item,
195189
xmlserialize(content act.row_data no indent) act_item
196190
from
197191
(select ucd.*, row_number() over(partition by pk_hash order by row_hash) duplicate_no
198192
from
199193
(select ucd.column_value row_data,
200-
dbms_crypto.hash( value(ucd).getclobval(),3) row_hash,
201-
dbms_crypto.hash( extract(value(ucd), :join_by_xpath ).getClobVal(),3/*HASH_SH1*/) pk_hash
194+
r.item_hash row_hash,
195+
r.pk_hash ,
196+
ucd.column_value.getRootElement() col_name,
197+
ucd.column_value.getclobval() col_val
202198
from
203-
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
204-
from ut_compound_data_tmp ucd
199+
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_hash, i.pk_hash
200+
from ut_compound_data_tmp ucd,
201+
diff_info i
205202
where ucd.data_id = :self_guid
206-
and ucd.item_hash in (select i.item_hash from diff_info i)
203+
and ucd.item_hash = i.item_hash
207204
) r,
208-
table( xmlsequence( extract(r.item_data,'/*') ) ) ucd
205+
table( xmlsequence( extract(r.item_data,'/*/*') ) ) ucd
209206
) ucd
210207
) exp
211-
full outer join (
208+
join (
212209
select ucd.*, row_number() over(partition by pk_hash order by row_hash) duplicate_no
213210
from
214211
(select ucd.column_value row_data,
215-
dbms_crypto.hash( value(ucd).getclobval(),3/*HASH_SH1*/) row_hash,
216-
dbms_crypto.hash( extract(value(ucd), :join_by_xpath ).getClobVal(),3/*HASH_SH1*/) pk_hash
212+
r.item_hash row_hash,
213+
r.pk_hash ,
214+
ucd.column_value.getRootElement() col_name,
215+
ucd.column_value.getclobval() col_val
217216
from
218-
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
219-
from ut_compound_data_tmp ucd
217+
(select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_hash, i.pk_hash
218+
from ut_compound_data_tmp ucd,
219+
diff_info i
220220
where ucd.data_id = :other_guid
221-
and ucd.item_hash in (select i.item_hash from diff_info i)
221+
and ucd.item_hash = i.item_hash
222222
) r,
223-
table( xmlsequence( extract(r.item_data,'/*') ) ) ucd
223+
table( xmlsequence( extract(r.item_data,'/*/*') ) ) ucd
224224
) ucd
225225
) act
226226
on exp.pk_hash = act.pk_hash and exp.duplicate_no = act.duplicate_no
227-
where (exp.row_hash != act.row_hash and act.pk_hash = exp.pk_hash) or
228-
(
229-
(exp.pk_hash is null or act.pk_hash is null) and
230-
(exp.row_hash is null or act.row_hash is null)
231-
)
227+
where dbms_lob.compare(exp.col_val, act.col_val) != 0
232228
)
233-
)
234-
unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:'
235-
,miss_item as 'Missing:', ext_item as 'Extra:') )
229+
unpivot ( data_item for diff_type in (exp_item as 'Expected:', act_item as 'Actual:') )
236230
)
237-
order by 1, 2
238-
)
239-
where rownum <= :max_rows
231+
union all
232+
select case when exp.pk_hash is null then 'Extra:' else 'Missing:' end as diff_type,
233+
xmlserialize(content nvl(exp.item_data, act.item_data) no indent) diffed_row,
234+
coalesce(exp.pk_hash,act.pk_hash) pk_hash
235+
from (select extract(ucd.item_data,'/*/*') item_data,i.pk_hash
236+
from ut_compound_data_tmp ucd,
237+
diff_info i
238+
where ucd.data_id = :self_guid
239+
and ucd.item_hash = i.item_hash
240+
) exp
241+
full outer join (
242+
select extract(ucd.item_data,'/*/*') item_data,i.pk_hash
243+
from ut_compound_data_tmp ucd,
244+
diff_info i
245+
where ucd.data_id = :other_guid
246+
and ucd.item_hash = i.item_hash
247+
)act
248+
on exp.pk_hash = act.pk_hash
249+
where exp.pk_hash is null or act.pk_hash is null
250+
)
251+
) where rn <= :max_rows
240252
]'
241253
bulk collect into l_results
242254
using a_diff_id,
243-
a_join_by_xpath,a_exclude_xpath, a_include_xpath, a_expected_dataset_guid,
244-
a_join_by_xpath,a_exclude_xpath, a_include_xpath, a_actual_dataset_guid,
255+
a_exclude_xpath, a_include_xpath, a_expected_dataset_guid,
256+
a_exclude_xpath, a_include_xpath, a_actual_dataset_guid,
257+
a_expected_dataset_guid, a_actual_dataset_guid,
245258
a_max_rows;
246259
return l_results;
247260
end;
@@ -266,9 +279,10 @@ create or replace package body ut_compound_data_helper is
266279
s.column_value.getRootElement() col_name,
267280
s.column_value.getclobval() col_val
268281
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
269-
from ut_compound_data_tmp ucd
282+
from ut_compound_data_tmp ucd,
283+
diff_info i
270284
where ucd.data_id = :self_guid
271-
and ucd.item_no in (select i.item_no from diff_info i)
285+
and ucd.item_no = i.item_no
272286
) r,
273287
table( xmlsequence( extract(r.item_data,'/*/*') ) ) s
274288
) exp
@@ -277,9 +291,10 @@ create or replace package body ut_compound_data_helper is
277291
s.column_value.getRootElement() col_name,
278292
s.column_value.getclobval() col_val
279293
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
280-
from ut_compound_data_tmp ucd
294+
from ut_compound_data_tmp ucd,
295+
diff_info i
281296
where ucd.data_id = :other_guid
282-
and ucd.item_no in (select i.item_no from diff_info i)
297+
and ucd.item_no = i.item_no
283298
) r,
284299
table( xmlsequence( extract(r.item_data,'/*/*') ) ) s
285300
) act
@@ -328,8 +343,9 @@ create or replace package body ut_compound_data_helper is
328343
* Since its unordered search we cannot select max rows from diffs as we miss some comparision records
329344
* We will restrict output on higher level of select
330345
*/
346+
331347
execute immediate q'[with
332-
diff_info as (select item_hash from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid)
348+
diff_info as (select item_hash,duplicate_no from ut_compound_data_diff_tmp ucdc where diff_id = :diff_guid)
333349
select duplicate_no,
334350
diffed_type,
335351
diffed_row
@@ -346,25 +362,29 @@ create or replace package body ut_compound_data_helper is
346362
when act.row_hash is null then
347363
xmlserialize(content exp.row_data no indent)
348364
end diffed_row
349-
from (select ucd.*, row_number() over(partition by row_hash order by row_hash) duplicate_no
365+
from (select ucd.*
350366
from (select ucd.column_value row_data,
351-
dbms_crypto.hash( value(ucd).getclobval(),3) row_hash
352-
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
353-
from ut_compound_data_tmp ucd
367+
r.item_hash row_hash,
368+
r.duplicate_no
369+
from (select ]'||l_column_filter||q'[, ucd.item_no, i.item_hash, i.duplicate_no
370+
from ut_compound_data_tmp ucd,
371+
diff_info i
354372
where ucd.data_id = :self_guid
355-
and ucd.item_hash in (select i.item_hash from diff_info i)
373+
and ucd.item_hash = i.item_hash
356374
) r,
357375
table( xmlsequence( extract(r.item_data,'/*') ) ) ucd
358376
) ucd
359377
) exp
360378
full outer join
361-
(select ucd.*, row_number() over(partition by row_hash order by row_hash) duplicate_no
379+
(select ucd.*
362380
from (select ucd.column_value row_data,
363-
dbms_crypto.hash( value(ucd).getclobval(),3/*HASH_SH1*/) row_hash
364-
from (select ]'||l_column_filter||q'[, ucd.item_no, ucd.item_data item_data_no_filter
365-
from ut_compound_data_tmp ucd
381+
r.item_hash row_hash,
382+
r.duplicate_no
383+
from (select ]'||l_column_filter||q'[, ucd.item_no, i.item_hash, i.duplicate_no
384+
from ut_compound_data_tmp ucd,
385+
diff_info i
366386
where ucd.data_id = :other_guid
367-
and ucd.item_hash in (select i.item_hash from diff_info i)
387+
and ucd.item_hash = i.item_hash
368388
) r,
369389
table( xmlsequence( extract(r.item_data,'/*') ) ) ucd
370390
) ucd

source/expectations/data_values/ut_compound_data_tmp.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ 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),
1920
duplicate_no integer,
2021
constraint ut_cmp_data_tmp_hash_pk unique (data_id,item_no, item_hash , duplicate_no)
2122
) on commit preserve rows;

0 commit comments

Comments
 (0)