I have the below data example for some items, those items have reference and reference parents. My issue is some references have wrong parents,which lead to infinite loop
let me explain, item with reference K001 doesn't have reference because it is the main product, reference k002 its parent is k001. k003 its parents is k001, data here is correct, because k001 is the parent with reference First.
For type B data is incorrect, m992 its parent is m993, however reference m993 its parent is m992, this is incorrect, the reference should point to a reference_parent First so to fix this the reference_parent for reference m992 should be m991 not m993.
For type C data also is incorrect, reference K883 has wrong reference_Parent because it hask886.
Because when I go to reference K886 I check its reference parent which is k885 then i check k885 reference parent which is k884 , then i check reference parent for k884 which is k883, then when I check k883 reference parent it gives me k886, here its wrong and I go an infinite loop, because k886 reference parent is kk85. To correct the reference parent of item k883 should be k882
Type Reference Reference_Parent
A K001 First
A K002 K001
A K003 K001
B M991 First
B M992 M993 --wrong parent data
B M993 M992
C K881 First
C K882 K881
C K883 K886 --wrong parent data
C K884 K883
C K885 K884
C K886 K885
A L001 First
A L002 L001
A L003 L002
A L004 L002
A L005 L002
A L006 L004
I tried with below query but its not giving me my required result and I am not expert with heirachy queries
SELECT reference,reference_parent,LEVEL ,SYS_CONNECT_BY_PATH(reference, '/') "Path"
FROM ITEMS
WHERE TYPE='B'
START WITH reference_parent = 'First'
CONNECT BY nocycle PRIOR reference_parent = 'First'
My required result is to give me
B M992 M993 --wrong parent data
C K883 K886 --wrong parent data
Oracle Setup:
CREATE TABLE ITEMS ( Type, Reference, Reference_Parent ) AS
SELECT 'A', 'K001', 'First' FROM DUAL UNION ALL
SELECT 'A', 'K002', 'K001' FROM DUAL UNION ALL
SELECT 'A', 'K003', 'K001' FROM DUAL UNION ALL
SELECT 'B', 'M991', 'First' FROM DUAL UNION ALL
SELECT 'B', 'M992', 'M993' FROM DUAL UNION ALL --wrong parent data
SELECT 'B', 'M993', 'M992' FROM DUAL UNION ALL
SELECT 'C', 'K881', 'First' FROM DUAL UNION ALL
SELECT 'C', 'K882', 'K881' FROM DUAL UNION ALL
SELECT 'C', 'K883', 'K886' FROM DUAL UNION ALL --wrong parent data
SELECT 'C', 'K884', 'K883' FROM DUAL UNION ALL
SELECT 'C', 'K885', 'K884' FROM DUAL UNION ALL
SELECT 'C', 'K886', 'K885' FROM DUAL UNION ALL
SELECT 'A', 'L001', 'First' FROM DUAL UNION ALL
SELECT 'A', 'L002', 'L001' FROM DUAL UNION ALL
SELECT 'A', 'L003', 'L002' FROM DUAL UNION ALL
SELECT 'A', 'L004', 'L002' FROM DUAL UNION ALL
SELECT 'A', 'L005', 'L002' FROM DUAL UNION ALL
SELECT 'A', 'L006', 'L004' FROM DUAL;
Query:
SELECT DISTINCT
MIN( CONNECT_BY_ROOT( Reference_Parent ) )
KEEP ( DENSE_RANK FIRST ORDER BY CONNECT_BY_ROOT( Reference ) )
AS reference_parent,
MIN( CONNECT_BY_ROOT( Reference ) )
AS reference
FROM ITEMS
START WITH Reference NOT IN (
SELECT reference
FROM ITEMS
START WITH Reference_Parent = 'First'
CONNECT BY PRIOR Reference = Reference_Parent
)
CONNECT BY NOCYCLE PRIOR Reference = Reference_Parent
GROUP BY Reference
Output:
REFERENCE_PARENT | REFERENCE
:--------------- | :--------
K886 | K883
M993 | M992
db<>fiddle here
Explanation:
You can find all the rows in the hierarchy starting with 'First' using the query:
SELECT reference
FROM ITEMS
START WITH Reference_Parent = 'First'
CONNECT BY PRIOR Reference = Reference_Parent
Your rows will be disconnected from these so you want a hierarchical query like:
SELECT *
FROM ITEMS
START WITH Reference NOT IN (
SELECT reference
FROM ITEMS
START WITH Reference_Parent = 'First'
CONNECT BY PRIOR Reference = Reference_Parent
)
CONNECT BY NOCYCLE PRIOR Reference = Reference_Parent
This just gives you the disconnected components and will generate lots of duplicate rows as it will start the hierarchy at every point in the cycle so some additional work is required to find the lowest Reference in the cycle (which appears to be where you are saying the wrong parents have occurred). This can be found by getting the MINimum values for CONNECT_BY_ROOT( Reference ) for each Reference; which gives you the query above.
Query 2:
You could also just look for cycles without eliminating the rows connected to 'First':
SELECT DISTINCT
MIN( Type ) KEEP ( DENSE_RANK FIRST ORDER BY Reference )
AS Type,
MIN( Reference ) AS Reference,
MIN( Reference_Parent ) KEEP ( DENSE_RANK FIRST ORDER BY Reference )
AS Reference_Parent
FROM (
SELECT Type,
Reference,
Reference_parent,
CONNECT_BY_ROOT( Reference_Parent ) AS root_reference_parent,
CONNECT_BY_ROOT( Reference ) AS root_reference,
CONNECT_BY_ISCYCLE AS isCycle
FROM ITEMS
CONNECT BY NOCYCLE PRIOR Reference = Reference_Parent
)
GROUP BY
root_Reference,
root_Reference_Parent
HAVING MAX( isCycle ) = 1
db<>fiddle here
Related
I have a table with parent/child ids, and I'm trying to get a full list of all levels of parents AND children for a given id.
Basically, for a given id, go all the way down and all the way up the hierarchy.
I've tried connect by, but maybe a recursive CTE would be better?
select 'abc' as child, null as parent from dual union all
select 'mno' as child, 'abc' as parent from dual union all
select 'def' as child, 'abc' as parent from dual union all
select '123' as child, 'abc' as parent from dual union all
select 'qrs' as child, '123' as parent from dual union all
select '789' as child, 'def' as parent from dual union all
select 'xyz' as child, '123' as parent from dual
Ex:
Child
Parent
abc
null
mno
abc
def
abc
123
abc
qrs
123
789
def
xyz
123
For 123, the desired output:
xyz > 123 > abc
qrs > 123 > abc
For abc, the desired output:
xyz > 123 > abc
789 > def > abc
qrs > 123 > abc
mno > abc
Here's my attempt. It seems kind of hacky with the full_hier being a concatenation + substring of the child & parent paths. Plus, I'm getting additional results that I'm not sure how to filter out (Ex: def > abc is returned though I don't want it as it's captured in 789 > def > abc).
select
connect_by_root child,
substr(sys_connect_by_path(child, '>' ),2) as child_hier
, substr(sys_connect_by_path(parent, '>' ),2) as parent_hier
, case
when parent is null then substr(sys_connect_by_path(child, '>' ),2)
else substr(sys_connect_by_path(child, '>' ),2) || substr(substr(sys_connect_by_path(parent, '>' ),2), instr(substr(sys_connect_by_path(parent, '>' ),2),'>',1,1))
end as full_hier
, level
from
(
select 'abc' as child, null as parent from dual union all
select 'mno' as child, 'abc' as parent from dual union all
select 'def' as child, 'abc' as parent from dual union all
select '123' as child, 'abc' as parent from dual union all
select 'qrs' as child, '123' as parent from dual union all
select '789' as child, 'def' as parent from dual union all
select 'xyz' as child, '123' as parent from dual
) table_name
where 1=1
--and connect_by_isleaf = 1
--and connect_by_root child in ('123')
and child = 'abc'
connect by child = prior parent
--connect_by prior parent = child
Thanks for taking a look, I appreciate it!
Another method.
This time via recursive CTE's.
with cte_init (base) as (
select '123' as base
from dual
),
rcte_hierarchy_down (base, lvl, child, parent) as (
select
child as base
, 0 as lvl
, child
, parent
from test_hierarchy
where child in (select base from cte_init)
union all
select
cte.base
, cte.lvl-1
, t.child
, t.parent
from rcte_hierarchy_down cte
join test_hierarchy t
on t.child = cte.parent
),
rcte_hierarchy_up (lvl, child, parent, path) as (
select
1 as lvl
, child
, parent
, child||'>'||parent as path
from test_hierarchy h
where parent in (select child
from rcte_hierarchy_down
where parent is null)
union all
select
cte.lvl+1
, t.child
, t.parent
, t.child||'>'||cte.path
from rcte_hierarchy_up cte
join test_hierarchy t
on t.parent = cte.child
)
select distinct h.path
from rcte_hierarchy_up h
join cte_init i on h.path like '%'||i.base||'%'
and not exists (
select 1
from test_hierarchy t
where t.parent = h.child
)
PATH
qrs>123>abc
xyz>123>abc
Demo on db<>fiddle here
If I understand correctly, given any id (as an input - below I use a bind variable in the query), you need to find all its leaf descendants, and then for each such leaf, show the full path from leaf to the root of the hierarchy.
One way to do that is to traverse the hierarchy twice: first start from the given id and find all its leaf descendants, then traverse in the opposite direction to find all the "full paths".
While this may look (marginally) more elegant, it will be significantly less efficient. The better approach is along the lines you were trying already.
Below I use the with clause (and give column names in the subquery declaration - that is only supported since Oracle 11.2, if your version is 11.1 you will need to move the aliases into the select clauses as you were doing in your attempt).
with
table_name (child, parent) as (
select 'abc', null from dual union all
select 'mno', 'abc' from dual union all
select 'def', 'abc' from dual union all
select '123', 'abc' from dual union all
select 'qrs', '123' from dual union all
select '789', 'def' from dual union all
select 'xyz', '123' from dual
)
, a (ancestor_path) as (
select sys_connect_by_path(child, '>')
from table_name
where connect_by_isleaf = 1
start with child = :i_child
connect by child = prior parent
)
, d (descendant_path) as (
select substr(sys_connect_by_path(child, '>'), 2)
from table_name
where connect_by_isleaf = 1
start with parent = :i_child
connect by parent = prior child
)
select d.descendant_path || a.ancestor_path as full_path
from d cross join a
;
Here's a method that first descends down the tree to get the root parent(s) from the chosen one.
Then uses those roots to climb back up.
The resulting paths are then filtered on the chosen child.
And what remains are those where the final child isn't a parent.
create table test_hierarchy (
child varchar(3),
parent varchar(3),
primary key (child)
);
insert into test_hierarchy (child, parent)
select 'abc' as child, null as parent from dual union all
select 'mno' as child, 'abc' as parent from dual union all
select 'def' as child, 'abc' as parent from dual union all
select '123' as child, 'abc' as parent from dual union all
select 'qrs' as child, '123' as parent from dual union all
select '789' as child, 'def' as parent from dual union all
select 'xyz' as child, '123' as parent from dual;
with cte (base) as (
select '123' from dual
)
select *
from
(
select
sys_connect_by_path(child,'>') as path
, cte.base
, level
, connect_by_root child as child
from test_hierarchy
cross join cte
where child in (
select connect_by_root child
from test_hierarchy
where child in (select base from cte)
and connect_by_root parent is null
connect by prior child = parent
)
connect by prior parent = child
) q
where path like '%'||base||'%'
and not exists (
select 1
from test_hierarchy t
where t.parent = q.child
)
PATH
BASE
LEVEL
CHILD
>qrs>123>abc
123
3
qrs
>xyz>123>abc
123
3
xyz
Demo on db<>fiddle here
I have a table with data of parent and child.
Parent | Child
---------------
A B
B C
C D
I want to get all the children form this table when I put where condition for parent.
ex -
select * from view where parent = 'A'
result should be B,C,D.
How do I write a view to get this output.
Thanks in advance.
Here's one option:
SQL> with test (parent, child) as
2 (select 'A', 'B' from dual union all
3 select 'B', 'C' from dual union all
4 select 'C', 'D' from dual
5 )
6 select max(ltrim(sys_connect_by_path(child, '-'), '-')) result
7 from test
8 connect by prior child = parent
9 start with parent = 'A';
RESULT
---------------------------------------------------------------------------
B-C-D
SQL>
You can do it using Listagg and Hierarchy clause
SQLFiddle
SELECT LISTAGG(CHILD, ',') WITHIN GROUP (ORDER BY CHILD) FROM (
SELECT CHILD FROM TABLE1 START WITH PARENT = 'A' CONNECT BY PRIOR CHILD = PARENT);
Let's say I have something similar to this (hierarchy of folders on drive C:).
I want to get top folder of a given folder (in this case I chose '1'), not the disc itself, how do I do this?
Hierarchy can have various levels.
with data as
(
select '1' name, 'folder' type, 'docs' parent from dual union
select '2' name, 'folder' type, 'docs' parent from dual union
select '3' name, 'folder' type, 'docs' parent from dual union
select 'docs' name, 'folder' type, 'MyFolder' parent from dual union
select 'MyFolder' name, 'folder' type, 'C:\' parent from dual union
select 'C:\' name, 'Drive' type, null parent from dual
)
select name, level from data
start with name = '1'
connect by prior parent = name
order by level;
expected output: 'Myfolder', as it doesn't have any other folder as parent.
Here's one option:
SQL> with data as
2 (
3 select '1' name , 'folder' type, 'docs' parent from dual union
4 select '2' name , 'folder' type, 'docs' parent from dual union
5 select '3' name , 'folder' type, 'docs' parent from dual union
6 select 'docs' name , 'folder' type, 'MyFolder' parent from dual union
7 select 'MyFolder' name, 'folder' type, 'C:\' parent from dual union
8 select 'C:\' name , 'Drive' type, null parent from dual
9 ),
10 inter as
11 (select name, level lvl, type
12 from data
13 start with name = '1'
14 connect by prior parent = name
15 )
16 select name
17 from inter
18 where lvl = (select max(lvl) from inter
19 where type = 'folder');
NAME
--------
MyFolder
SQL>
Please try the below,
WITH data
AS (SELECT '1' name, 'folder' TYPE, 'docs' parent FROM DUAL
UNION
SELECT '2' name, 'folder' TYPE, 'docs' parent FROM DUAL
UNION
SELECT '3' name, 'folder' TYPE, 'docs' parent FROM DUAL
UNION
SELECT 'docs' name, 'folder' TYPE, 'MyFolder' parent FROM DUAL
UNION
SELECT 'MyFolder' name, 'folder' TYPE, 'C:\' parent FROM DUAL
UNION
SELECT 'C:\' name, 'Drive' TYPE, NULL parent FROM DUAL)
SELECT name
FROM data
WHERE parent = (SELECT name
FROM data
WHERE TYPE = 'Drive')
START WITH name = '1'
CONNECT BY PRIOR parent = name
Output
NAME
--------
MyFolder
Test with the real data and let us know the outcome.
I have hierarchical data with the following structure. The levels are 1 to 6, so instead of doing 6 joins and then coalesce, how can I find what is the top-level parent (which has no parent_nr).
I've tried the accepted answer here,
SELECT
aa.code_nr,
aa.parent_nr,
CONNECT_BY_ROOT aa.code_nr AS "Top Level ID"
FROM mytable_t aa
CONNECT BY PRIOR aa.code_nr = aa.parent_nr
;
but it only gives me the next level as "Top Level ID" and not the final level (A)
Oracle Setup:
CREATE TABLE my_table ( code_nr, parent_nr ) AS (
SELECT 'A', NULL FROM DUAL UNION ALL
SELECT 'A.1', 'A' FROM DUAL UNION ALL
SELECT 'A.1.1', 'A.1' FROM DUAL UNION ALL
SELECT 'A.1.1.1', 'A.1.1' FROM DUAL UNION ALL
SELECT 'A.1.1.2', 'A.1.1' FROM DUAL UNION ALL
SELECT 'A.1.1.1.1', 'A.1.1.1' FROM DUAL UNION ALL
SELECT 'A.1.1.2.1', 'A.1.1.2' FROM DUAL UNION ALL
SELECT 'A.1.1.2.2', 'A.1.1.2' FROM DUAL;
Query:
SELECT LEVEL,
code_nr AS root_code_nr,
CONNECT_BY_ROOT( code_nr ) AS code_nr
FROM my_table
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY PRIOR parent_nr = code_nr;
Output:
LEVEL ROOT_CODE CODE_NR
----- --------- ---------
1 A A
2 A A.1
3 A A.1.1
4 A A.1.1.1
5 A A.1.1.1.1
4 A A.1.1.2
5 A A.1.1.2.1
5 A A.1.1.2.2
You could try the following:
SELECT
aa.code_nr,
aa.parent_nr,
substr(SYS_CONNECT_BY_PATH(aa.code_nr, '/'),2,instr(SYS_CONNECT_BY_PATH(aa.code_nr, '/'),'/'))
FROM mytable_t aa
CONNECT BY PRIOR aa.code_nr = aa.parent_nr
;
I want to find duplicate of IRN # entered into a table in database. Here are the unique attributes (logically unique) of the IRN.
ProjectNo, DrawingNo, DrawingRev, SpoolNo, WeldNo
An IRN can have multiple WeldNos meaning the above unique attributes may repeat for one IRN # (with of course one of the 5 attribute values must be unique).
Now I am trying to find out whether there are any duplicate IRNs entered into the system or not? How can I find that through a sql query?
P.S: Due to bad design of database, there is no primary key in the table..
Here is what I have tried so far but this does not give the correct results.
select * from WeldInfo a, WeldInfo b
where a.ProjectNo = b.ProjectNo and
a.DrawingNo = b.DrawingNo and
a.DrawingRev = b.DrawingRev and
a.SpoolNo = b.SpoolNo and
a.WeldNo = b.WeldNo and
a.IrnNo <> b.IrnNo;
But i'm not sure, have i understood your question.
select * from (
select count(*) over ( partition by ProjectNo, DrawingNo, DrawingRev, SpoolNo, WeldN) rr,t.* from WeldInfo t)
where rr > 1;
Explanation.
with tab as (
select 1 as id, 'a' as a , 'b' as b , 'c' as c from dual
union all
select 2 , 'a', 'b', 'c' from dual
union all
select 3 , 'x', 'b', 'c' from dual
union all
select 3 , 'x', 'b', 'c' from dual
union all
select 3 , 'x', 'd', 'c' from dual
)
select t.*
, count(*) over (partition by a,b,c) cnt1
, count(distinct id) over (partition by a,b,c) cnt2
from tab t;