Get all parents for given element and their siblings - sql

I got some table with hierarchy:
create table t_hier (id number primary key, parent number);
insert into t_hier (id, parent) values(0, null);
insert into t_hier (id, parent) values(1, 0);
insert into t_hier (id, parent) values(2, 0);
insert into t_hier (id, parent) values(3, 1);
insert into t_hier (id, parent) values(4, 1);
insert into t_hier (id, parent) values(5, 2);
insert into t_hier (id, parent) values(6, 2);
insert into t_hier (id, parent) values(7, 5);
insert into t_hier (id, parent) values(8, 5);
select rpad('* ', 2*level, '* ')||id id, parent
from t_hier
connect by prior id = parent
start with parent is null;
ID PARENT
____________________ ______
* 0
* * 1 0
* * * 3 1
* * * 4 1
* * 2 0
* * * 5 2
* * * * 7 5
* * * * 8 5
* * * 6 2
Given some ID I need to get all its parents, grandparents etc and also every sibling of returned elements (by siblings I mean only elements with the same parent, not the entire level), and also given element itself.
So if I have element with id 5, I need to return 0, 1, 2, 5 and 6.
For element with id 7 I need to return 0, 1, 2, 5, 6, 7, 8.
I think it can be done by just one query, it will be great if someone will help me with it.

with parents as (
select level lvl, id
from t_hier
start with id = 7
connect by id = prior parent
)
select distinct id from t_hier
where id != 7
start with id in (select id from parents where lvl > 1)
connect by prior id = parent and level <= 2;
Find all forefathers
Go back and for each forefather find his children but only on the second level
Exclude the starting id.

This may help :
with cte_getHierarchy (id,parent)
as
(
select t_hier.id,t_hier.parent from t_hier where id = 7
union all
select t_hier.id,t_hier.parent from t_hier join cte_getHierarchy on t_hier.id = cte_getHierarchy.parent
),
cte_getsibling (id,parent)
as
(
select cte_getHierarchy.id,cte_getHierarchy.parent from cte_getHierarchy
union
select t_hier.id,t_hier.parent from t_hier join cte_getHierarchy on t_hier.parent = cte_getHierarchy.parent
)
select id from cte_getsibling where id <> 7;
sql fiddle

Should be this one:
select rpad('* ', 2*level, '* ')||id id, parent
from t_hier
connect by id = prior parent
start with id = 3
union
select rpad('* ', 2*level, '* ')||id id, parent
from t_hier
connect by prior id = parent and level = 1
start with parent = (select parent from t_hier where id = 3);
Why should id 7 return 0, 1(???), 2, 5, 6, 8.
1 is neither ancestor of 7 nor a sibling.

Related

Oracle copying the tree in the table

I'm looking for some interesting simple algorithm to copy (clone) part of the tree to another part of the tree.
We have a table:
create table tree (
id integer not null,
parent_id integer,
value varchar2(255),
CONSTRAINT tab_pk PRIMARY KEY (id)
);
begin
insert into tree(id, parent_id, value) values (1, null, 'A');
insert into tree(id, parent_id, value) values (2, 1, 'B');
insert into tree(id, parent_id, value) values (3, 1, 'C');
insert into tree(id, parent_id, value) values (4, 2, 'BC');
insert into tree(id, parent_id, value) values (8, 4, 'BCX');
insert into tree(id, parent_id, value) values (5, 2, 'BD');
insert into tree(id, parent_id, value) values (6, 3, 'CA');
insert into tree(id, parent_id, value) values (7, 3, 'CD');
end;
/
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=9fe802f144a3af0663754cfb3e8dc1ba
How to easily copy a tree "B" (ID = 2) with all children under "CA" (ID = 6)?
ORACLE 18-19c.
I tried to understand your question
Your original query provides this output
select level, id,
lpad ( ' ', level, ' ' ) || value name
from tree
start with parent_id is null
connect by prior id = parent_id;
LE ID NAME
1 1 A
2 2 B
3 4 BC
4 8 BCX
3 5 BD
2 3 C
3 6 CA
3 7 CD
Then you say you want the tree "B" (ID = 2) with all children under "CA" (ID = 6)
select level, id,
lpad ( ' ', level, ' ' ) || value name
from tree
start with id = 2
connect by prior id = parent_id
union all
-- tree to clone
select level, id,
lpad ( ' ', level, ' ' ) || value name
from tree
start with id = 6 or id=7
connect by prior id = parent_id;
LE ID NAME
1 2 B
2 4 BC
3 8 BCX
2 5 BD
1 6 CA
1 7 CD
db<>fiddle

SQL combine foreign key values of all parents into child

In an informix database, I have a hierarchical tree structure, where a child entry can have any level of parents (parent, grandparent, etc). via relation to its parent entry.
Every entry has a collection of attribute names and values.
The tables are modeled is as follows:
node:
+-------------+----------------------+--------+
| id | parn_id | name |
+-------------+----------------------+--------+
| int | int | string |
| primary key | existing id, or null | |
+-------------+----------------------+--------+
vals:
+-----------------------+-------------+---------+
| id | atr_id | atr_val |
+-----------------------+-------------+---------+
| int | int | string |
| foreign key from node | primary key | |
+-----------------------+-------------+---------+
look:
+-----------------------+--------+
| atr_id | name |
+-----------------------+--------+
| int | string |
| foreign key from vals | |
+-----------------------+--------+
I need a sql query which returns all of the parent's (vals, look) pairs when asking for a child.
For example, if I have
Parent: (valP, nameP), (valP2, nameP2)
*
* * * Child (valC, nameC)
*
* * * GrandChild (valGC, nameGC)
And I query for the GrandChild, I want it to return:
GrandChild (valP, nameP), (valP2, nameP2), (valC, nameC), (valGC, nameGC)
Using a recent Informix version ( I am using Informix 14.10.FC1 ) you can use the CONNECT BY clause to work with hierarchical queries.
The setup, based on your description:
CREATE TABLE node
(
id INTEGER PRIMARY KEY,
parn_id INTEGER,
name CHAR( 20 ) NOT NULL
);
INSERT INTO node VALUES ( 1, NULL, 'Node_A' );
INSERT INTO node VALUES ( 2, NULL, 'Node_B' );
INSERT INTO node VALUES ( 3, NULL, 'Node_C' );
INSERT INTO node VALUES ( 4, 2, 'Node_D' );
INSERT INTO node VALUES ( 5, 3, 'Node_E' );
INSERT INTO node VALUES ( 6, 3, 'Node_F' );
INSERT INTO node VALUES ( 7, 4, 'Node_G' );
CREATE TABLE vals
(
id INTEGER NOT NULL REFERENCES node( id ),
atr_id INTEGER PRIMARY KEY,
atr_val CHAR( 20 ) NOT NULL
);
INSERT INTO vals VALUES ( 1, 1, 'Value_A_1' );
INSERT INTO vals VALUES ( 2, 2, 'Value_B_1' );
INSERT INTO vals VALUES ( 2, 3, 'Value_B_2' );
INSERT INTO vals VALUES ( 3, 4, 'Value_C_1' );
INSERT INTO vals VALUES ( 3, 5, 'Value_C_2' );
INSERT INTO vals VALUES ( 4, 6, 'Value_D_1' );
INSERT INTO vals VALUES ( 5, 7, 'Value_E_1' );
INSERT INTO vals VALUES ( 5, 8, 'Value_E_2' );
INSERT INTO vals VALUES ( 6, 9, 'Value_F_1' );
INSERT INTO vals VALUES ( 7, 10, 'Value_G_1' );
CREATE TABLE look
(
atr_id INTEGER NOT NULL REFERENCES vals( atr_id ),
name CHAR( 20 ) NOT NULL
);
INSERT INTO look VALUES ( 1, 'Look_A_1' );
INSERT INTO look VALUES ( 2, 'Look_B_1' );
INSERT INTO look VALUES ( 3, 'Look_B_2' );
INSERT INTO look VALUES ( 4, 'Look_C_1' );
INSERT INTO look VALUES ( 5, 'Look_C_2' );
INSERT INTO look VALUES ( 6, 'Look_D_1' );
INSERT INTO look VALUES ( 7, 'Look_E_1' );
INSERT INTO look VALUES ( 8, 'Look_E_2' );
INSERT INTO look VALUES ( 9, 'Look_F_1' );
INSERT INTO look VALUES ( 10, 'Look_G_1' );
we can use the CONNECT BY to find the child parents, for example:
-- Starting from 'Node_G'
SELECT
n.id,
n.parn_id,
n.name,
CONNECT_BY_ROOT n.name AS starting_node
FROM
node AS n
START WITH n.name = 'Node_G'
CONNECT BY PRIOR n.parn_id = n.id
ORDER BY
n.name
;
-- RESULTS:
id parn_id name starting_node
2 Node_B Node_G
4 2 Node_D Node_G
7 4 Node_G Node_G
And then we can join with the attribute tables:
SELECT
vt1.starting_node,
v.atr_val,
l.name
FROM
(
SELECT
n.id,
n.parn_id,
n.name,
CONNECT_BY_ROOT n.name AS starting_node
FROM
node AS n
START WITH n.name = 'Node_G'
CONNECT BY PRIOR n.parn_id = n.id
) AS vt1
INNER JOIN vals AS v
ON
v.id = vt1.id
INNER JOIN look AS l
ON
l.atr_id = v.atr_id
ORDER BY
vt1.starting_node, v.atr_val
;
-- RESULTS:
starting_node atr_val name
Node_G Value_B_1 Look_B_1
Node_G Value_B_2 Look_B_2
Node_G Value_D_1 Look_D_1
Node_G Value_G_1 Look_G_1
If we remove the START WITH clause, we get the hierarchical results for each node:
SELECT
vt1.starting_node,
v.atr_val,
l.name
FROM
(
SELECT
n.id,
n.parn_id,
n.name,
CONNECT_BY_ROOT n.name AS starting_node
FROM
node AS n
CONNECT BY PRIOR n.parn_id = n.id
) AS vt1
INNER JOIN vals AS v
ON
v.id = vt1.id
INNER JOIN look AS l
ON
l.atr_id = v.atr_id
ORDER BY
vt1.starting_node, v.atr_val
;
-- RESULTS:
starting_node atr_val name
Node_A Value_A_1 Look_A_1
Node_B Value_B_1 Look_B_1
Node_B Value_B_2 Look_B_2
Node_C Value_C_1 Look_C_1
Node_C Value_C_2 Look_C_2
Node_D Value_B_1 Look_B_1
Node_D Value_B_2 Look_B_2
Node_D Value_D_1 Look_D_1
Node_E Value_C_1 Look_C_1
Node_E Value_C_2 Look_C_2
Node_E Value_E_1 Look_E_1
Node_E Value_E_2 Look_E_2
Node_F Value_C_1 Look_C_1
Node_F Value_C_2 Look_C_2
Node_F Value_F_1 Look_F_1
Node_G Value_B_1 Look_B_1
Node_G Value_B_2 Look_B_2
Node_G Value_D_1 Look_D_1
Node_G Value_G_1 Look_G_1

SQL hierarchical query: find full tree given an id of any parent-child (Oracle)

I'm struggling a hierarchical SQL query (using Oracle). Say I have table of widgets with unique ids. A widget can be copied only once, and we track the parent id from the new widget. For example, lets say widget 100 gets copied and becomes widget 101, then 103, then 105.
id parent_id
100
101 100
102
103 101
104
105 103
CREATE TABLE WIDGETS (ID NUMBER NOT NULL, PARENT_ID NUMBER);
INSERT INTO WIDGETS (ID, PARENT_ID) VALUES (100, null);
INSERT INTO WIDGETS (ID, PARENT_ID) VALUES (101, 100);
INSERT INTO WIDGETS (ID, PARENT_ID) VALUES (102, null);
INSERT INTO WIDGETS (ID, PARENT_ID) VALUES (103, 101);
INSERT INTO WIDGETS (ID, PARENT_ID) VALUES (104, null);
INSERT INTO WIDGETS (ID, PARENT_ID) VALUES (105, 103);
(102 and 104 is just dummy data)
I'm trying to make a query that displays the full history of a widget, using any of the ids in the relationship.
So if I give the query an id of '103' (or '100'... or '105'), I'd expect the query to return something like this:
id parent_id
100
101 100
103 101
105 103
I've tried using CONNECT BY PRIOR, but I would need the start by ID to use start with. For example, I can display the full tree giving the query the origin id:
select parent_id as from_id, id as to_id
from WIDGETS
start with id = 100
CONNECT BY PRIOR id = parent_id;
from_id to_id
null 100
100 101
101 103
103 105
But what if I don't necessarily know the start with id? Is it possible to find the origin ID and then get the full tree from there?
Calculate the start by traversing the tree backwards until you hit a leaf.
SELECT parent_id AS from_id, ID AS to_id
FROM WIDGETS
START WITH ID =
( SELECT ID
FROM WIDGETS
WHERE CONNECT_BY_ISLEAF=1
START WITH ID = 105
CONNECT BY ID = PRIOR parent_id
)
CONNECT BY PRIOR ID = parent_id;
#Input below is the input id you pass to Stored Procedure. Try this, hope it helps:
DECLARE #Parent int;
WHILE (Select #Parent = PARENT_ID From Widgets Where ID = #Input) Is NOT NULL
BEGIN
Set #Input = #Parent
END
Set #Parent = #Input
declare #Hierarchy table(Member int)
INSERT INTO #Hierarchy(Member) Values (#Parent)
WHILE (Select #Input = ID From Widgets Where PARENT_ID = #Parent) Is NOT NULL
BEGIN
SET #Parent = #Input
INSERT INTO #Hierarchy(Member) Values (#Parent)
END
Select * From #Hierarchy

Select all recursive value with one query SQLite

i have some another case :
first I created a table:
CREATE TABLE tree(
id_tree integer PRIMARY KEY AUTOINCREMENT,
id_boss TEXT,
id_child TEXT,
answ TEXT);
insert some values :
INSERT INTO tree(id_boss,id_child,answ) VALUES('1','8','T');
INSERT INTO tree(id_boss,id_child,answ) VALUES('1',null,'F');
INSERT INTO tree(id_boss,id_child,answ) VALUES('8','P1','T');
INSERT INTO tree(id_boss,id_child,answ) VALUES('8','2','F');
INSERT INTO tree(id_boss,id_child,answ) VALUES('2','P2','T');
INSERT INTO tree(id_boss,id_child,answ) VALUES('2','P3','F');
and execute query:
WITH RECURSIVE
ancestors(id, answ) AS (
VALUES('P3', 'T')
UNION ALL
SELECT tree.id_boss, tree.answ
FROM tree JOIN ancestors ON tree.id_child = ancestors.id
)
SELECT id FROM ancestors WHERE answ = 'T';
the result is :
P3
1
that for P3 , and i want to make list with all recursive value , so it will be like this :
1 --- // P3
1 --- // P1
8 --- // P1
2 --- // P2
1 --- // P2
Is this somewhere near what you are looking for?
WITH seed (id, answ) as ( VALUES('P3', 'T') )
, ancestors1(id, answ) AS (
select * from seed
UNION ALL
SELECT tree.id_boss, tree.answ
FROM tree, ancestors1 where tree.id_child = ancestors1.id
)
, ancestors2(id, answ) AS (
select * from seed
UNION ALL
SELECT tree.id_boss, tree.answ
FROM tree, ancestors2 where tree.id_child = ancestors2.id
)
select id from (
select * from ancestors1
union all
select * from ancestors2
);
I had to rephrase certain things inorder to get thenm to compile with db2 (remove RECURSIVE, and use implicit join)

get all child from an parent id

hi i need a query to do this
my table data
ID ParentID DATA
--------------------------------
1 -1 a
2 1 b
3 2 c
4 3 d
5 3 f
and what ineed a query that take a ID as parameter and return all recursively childs and Itself
parameter : (ID=2)
return must be :
ID ParentID DATA
--------------------------------
2 1 b
3 2 c
4 3 d
5 3 f
Try this:
;with temp as (
select id, parentId, data from t
where id = 2
union all
select t.id, t.parentId, t.data from t
join temp on temp.id = t.parentId
)
select * from temp
Fiddle here.
This should do it for you:
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
where topparent = 2
drop table #temp
EDIT or you can put the WHERE clause inside the first select
create table #temp
(
id int,
parentid int,
data varchar(1)
)
insert #temp (id, parentid, data) values (1, -1, 'a')
insert #temp (id, parentid, data) values (2,1, 'b')
insert #temp (id, parentid, data) values (3,2, 'c')
insert #temp (id, parentid, data) values (4,3, 'd')
insert #temp (id, parentid, data) values (5,3, 'f')
; with cte as (
select id, parentid, data, id as topparent
from #temp
WHERE id = 2
union all
select child.id, child.parentid, child.data, parent.topparent
from #temp child
join cte parent
on parent.id = child.parentid
)
select id, parentid, data
from cte
drop table #temp
Results:
id parentid data
2 1 b
3 2 c
4 3 d
5 3 f
declare #ID int = 2;
with C as
(
select ID, ParentID, DATA
from YourTable
where ID = #ID
union all
select T.ID, T.ParentID, T.DATA
from YourTable as T
inner join C
on T.ParentID = C.ID
)
select ID, ParentID, DATA
from C
Try on SE-Data
try this.
select * from table where id= 2 or parentid = 2