Identifying the transitive match records in oracle SQL - sql

SOURCE
Rowid_object
Rowid_object_matched
1
2
1
3
3
2
2
4
4
6
6
5
7
8
9
8
Target
Rowid_object
Rowid_object_matched
1
1
2
1
3
1
4
1
5
1
6
1
7
7
8
7
9
7
Here, we have Source like data and we want Target like result.
There are two groups in source which are in transitive match.
Need to identify these kind of record.

In Oracle, you can use:
SELECT rowid_object,
MIN(root) AS rowid_object_matched
FROM (
SELECT CONNECT_BY_ROOT(rowid_object) AS root,
rowid_object,
rowid_object_matched
FROM source
CONNECT BY NOCYCLE
PRIOR rowid_object IN (rowid_object, rowid_object_matched)
OR PRIOR rowid_object_matched IN (rowid_object, rowid_object_matched)
)
UNPIVOT (
rowid_object FOR key IN (rowid_object, rowid_object_matched)
)
GROUP BY rowid_object;
Which, for the sample data:
CREATE TABLE SOURCE (Rowid_object, Rowid_object_matched) AS
SELECT 1, 2 FROM DUAL UNION ALL
SELECT 1, 3 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 2, 4 FROM DUAL UNION ALL
SELECT 4, 6 FROM DUAL UNION ALL
SELECT 6, 5 FROM DUAL UNION ALL
SELECT 7, 8 FROM DUAL UNION ALL
SELECT 9, 8 FROM DUAL;
Outputs:
ROWID_OBJECT
ROWID_OBJECT_MATCHED
1
1
2
1
3
1
4
1
6
1
5
1
7
7
8
7
9
7
fiddle

Related

Reset Row Number in Oracle conditionally

I have this record set returned , now I want to have a row number column which gets reset after every 3rd row. Can anyone help me with this? needs to be done with Oracle SQL.
Explanation below-
data
current row number
rquired row number
Chris
1
1
Bryan
2
2
Jim
3
3
Davis
4
1
Kia
5
2
Jones
6
3
Mary
7
1
Carrie
8
2
Pearce
9
3
Cesar
10
1
Bob
11
2
You can mod the current value:
mod(current_row_num - 1, 3) + 1
So using a CTE to represent your current result set:
with your_result (data, current_row_num) as (
select 'Chris', 1 from dual union all
select 'Bryan', 2 from dual union all
select 'Jim', 3 from dual union all
select 'Davis', 4 from dual union all
select 'Kia', 5 from dual union all
select 'Jones', 6 from dual union all
select 'Mary', 7 from dual union all
select 'Carrie', 8 from dual union all
select 'Pearce', 9 from dual union all
select 'Cesar', 10 from dual union all
select 'Bob', 11 from dual
)
select data, current_row_num, mod(current_row_num - 1, 3) + 1 as required_row_num
from your_result
order by current_row_num
DATA
CURRENT_ROW_NUM
REQUIRED_ROW_NUM
Chris
1
1
Bryan
2
2
Jim
3
3
Davis
4
1
Kia
5
2
Jones
6
3
Mary
7
1
Carrie
8
2
Pearce
9
3
Cesar
10
1
Bob
11
2
db<>fiddle

List number into TABLE records by the value in another table

Sorry for a little bit confuse on the title.
I would like to use SQL for join sequence table as below
Original Table Records
3
3
4
1
Result Table
1
2
3
1
2
3
1
2
3
4
1
Based on comment you posted,
sample data:
SQL> with test (col) as
2 (select 3 from dual union all
3 select 3 from dual union all
4 select 4 from dual union all
5 select 1 from dual
6 )
Query begins here:
7 select column_value
8 from test cross join
9 table(cast(multiset(select level from dual
10 connect by level <= col
11 ) as sys.odcinumberlist));
COLUMN_VALUE
------------
1
2
3
1
2
3
1
2
3
4
1
11 rows selected.
SQL>

Oracle add sequential query rows if missing

In Oracle 19c I have a data like:
with t as (
select 1 Cat, 1 id, 11 val from dual
union all
select 1, 3, 33 from dual
union all
select 2, 2, 22 from dual
union all
select 2, 4, 44 from dual)
select *
from t
In query result I want to get 4 rows per every cat with ids 1-4 and if there was no such id in that cat a val must be null:
cat
id
val
1
1
11
1
2
1
3
33
1
4
2
1
2
2
22
2
3
2
4
44
Use a PARTITIONed join with a row-generator:
SELECT t.cat,
i.id,
t.val
FROM (SELECT LEVEL AS id FROM DUAL CONNECT BY LEVEL <= 4) i
LEFT OUTER JOIN t
PARTITION BY (t.cat)
ON (i.id = t.id)
Which outputs:
CAT
ID
VAL
1
1
11
1
2
null
1
3
33
1
4
null
2
1
null
2
2
22
2
3
null
2
4
44
db<>fiddle here

Flag individuals that share common features with Oracle SQL

Consider the following table:
ID Feature
1 1
1 2
1 3
2 3
2 4
2 6
3 5
3 10
3 12
4 12
4 18
5 10
5 30
I would like to group the individuals based on overlapping features. If two of these groups again have overlapping features, I would consider both as one group. This process should be repeated until there are no overlapping features between groups. The result of this procedure on the table above would be:
ID Feature Flag
1 1 A
1 2 A
1 3 A
2 3 A
2 4 A
2 6 A
3 5 B
3 10 B
3 12 B
4 12 B
4 18 B
5 10 B
5 30 B
So actually the problem I am trying to solve is finding connected components in a graph. Here [1,2,3] is the graph with ID 1 (see https://en.wikipedia.org/wiki/Connectivity_(graph_theory)). The problem is equivalent to this problem, however I would like to solve it with Oracle SQL.
Here is one way to do this, using a hierarchical ("connect by") query. The first step is to extract the initial relationships from the base data; the hierarchical query is built on the result from this first step. I added one more row to the inputs to illustrate a node that is a connected component by itself.
You marked the connected components as A and B - of course, that won't work if you have, say, 30,000 connected components. In my solution, I use the minimum node name as the marker for each connected component.
with
sample_data (id, feature) as (
select 1, 1 from dual union all
select 1, 2 from dual union all
select 1, 3 from dual union all
select 2, 3 from dual union all
select 2, 4 from dual union all
select 2, 6 from dual union all
select 3, 5 from dual union all
select 3, 10 from dual union all
select 3, 12 from dual union all
select 4, 12 from dual union all
select 4, 18 from dual union all
select 5, 10 from dual union all
select 5, 30 from dual union all
select 6, 40 from dual
)
-- select * from sample_data; /*
, initial_rel(id_base, id_linked) as (
select distinct s1.id, s2.id
from sample_data s1 join sample_data s2
on s1.feature = s2.feature and s1.id <= s2.id
)
-- select * from initial_rel; /*
select id_linked as id, min(connect_by_root(id_base)) as id_group
from initial_rel
start with id_base <= id_linked
connect by nocycle prior id_linked = id_base and id_base < id_linked
group by id_linked
order by id_group, id
;
Output:
ID ID_GROUP
------- ----------
1 1
2 1
3 3
4 3
5 3
6 6
Then, if you need to add the ID_GROUP as a FLAG to the base data, you can do so with a trivial join.

UNPIVOT the results of a delimited string on several rows

I am struggling with some complex hierarchical data. I have successfully used a CONNECT BY query to limit the rows down to the subset that i want - and i have used SYS_CONNECT_BY_PATH to return the full tree up to the nodes of interest.
this gives me essentially some rows like this (delimited by '|'):
id path
-------------------
1, '|10|11|12|13'
2, '|10|14|15'
3, '|16|11|12|13'
4, '|16|17'
now - my challenge is to unwrap or UNPIVOT these values back into a structure like this:
id ord node
-------------
1, 1, 10
1, 2, 11
1, 3, 12
1, 4, 13
2, 1, 10
2, 2, 14
2, 3, 15
3, 1, 16
3, 2, 11
3, 3, 12
3, 4, 13
4, 1, 16
4, 2, 17
I think i am unable to use UNPIVOT directly as that is working on a fixed set of columns - which this is not.
I am playing with a PIPELINE function to unwrap this, but frankly - passing all these rows to the function is an issue since they come from another query. I am wondering if anyone has a way to UNPIVOT the values from a SYS_CONNECT_BY_PATH result set back into rows that is maybe a pure sql solution - probably with REGEX parsing...
help always appreciated - thanks
Yes, UNPIVOT operator wont do much here to help you produce the desired output.
As one of the approaches you could user regexp_count()(11g R1 version and up) regular
expression function to count all occurrences of numbers and then use regexp_substr() regular
expression function to extract the numbers as follows:
-- sample of data
SQL> with t1(id1, path1) as(
2 select 1, '|10|11|12|13' from dual union all
3 select 2, '|10|14|15' from dual union all
4 select 3, '|16|11|12|13' from dual union all
5 select 4, '|16|17' from dual
6 ),
7 occurrences(ocr) as( -- occurrences
8 select level
9 from ( select max(regexp_count(path1, '[^|]+')) as mx_ocr
10 from t1
11 ) t
12 connect by level <= t.mx_ocr
13 )
14 select id1
15 , row_number() over(partition by id1 order by id1) as ord
16 , node
17 from ( select q.id1
18 , regexp_substr(q.path1, '[^|]+', 1, o.ocr) as node
19 from t1 q
20 cross join occurrences o
21 )
22 where node is not null
23 order by id1, 2, node
24 ;
Result:
ID1 ORD NODE
---------- ---------- ------------------------------------------------
1 1 10
1 2 11
1 3 12
1 4 13
2 1 10
2 2 14
2 3 15
3 1 11
3 2 12
3 3 13
3 4 16
4 1 16
4 2 17
13 rows selected
As another approach, starting from 10g version and up, you could use model clause:
SQL> with t1(id1, path1) as(
2 select 1, '|10|11|12|13' from dual union all
3 select 2, '|10|14|15' from dual union all
4 select 3, '|16|11|12|13' from dual union all
5 select 4, '|16|17' from dual
6 )
7 select id1
8 , ord
9 , node
10 from t1
11 model
12 partition by ( rownum as id1)
13 dimension by ( 1 as ord)
14 measures( path1
15 , cast(null as varchar2(11)) as node
16 , nvl(regexp_count(path1, '[^|]+'), 0) as ocr )
17 rules(
18 node[for ord from 1 to ocr[1] increment 1] =
19 regexp_substr(path1[1], '[^|]+', 1, cv(ord))
20 )
21 order by id1, ord, node
22 ;
Result:
ID1 ORD NODE
---------- ---------- -----------
1 1 10
1 2 11
1 3 12
1 4 13
2 1 10
2 2 14
2 3 15
3 1 16
3 2 11
3 3 12
3 4 13
4 1 16
4 2 17
13 rows selected
SQLFiddle Demo