Oracle copying the tree in the table - sql

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

Related

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

Recursive CTE to find all records SQL Server

I am having below table structure. I want to write a recursive cte to get the bottom most table result.
Highly appreciate your help.
CREATE TABLE Jobcard (
jobcard_id INT NOT NULL PRIMARY KEY,
jobcard_name varchar(20) NOT NULL,
);
CREATE TABLE Vehicle (
vehicle_id INT NOT NULL PRIMARY KEY,
vehicle_name varchar(20) NOT NULL
);
CREATE TABLE Jobacard_vehicle (
jobcard_id INT NOT NULL,
vehicle_id INT NOT NULL
);
INSERT INTO Jobcard (jobcard_id, jobcard_name) VALUES
(1, 'Job1'),(2, 'Job2'),(3, 'Job3'),
(4, 'Job4'),(5, 'Job5'),(6, 'Job6'),
(7, 'Job7'),(8, 'Job8'),(9, 'Job9');
INSERT INTO Vehicle (vehicle_id, vehicle_name) VALUES
(1, 'Vehicle1'),(2, 'Vehicle2'),(3, 'Vehicle3'),
(4, 'Vehicle4'),(5, 'Vehicle5'),(6, 'Vehicle6');
INSERT INTO Jobacard_vehicle (jobcard_id, vehicle_id) VALUES
(3, 1),(4, 2),(5, 3),
(9, 6),(7, 2),(5, 4),
(8, 4),(6, 1),(3, 5);
jobcard_id, vehicle_id
--------------------------
3 1
4 2
5 3
9 6
7 2
5 4
8 4
6 1
3 5
I want to get this result from Jobacard_vehicle table when I pass the vehicle id as 3 as
vehicle id 3 is having jobcard 5
jobcard 5 is having vehicle 4
again vehicle 4 referred to jobcard 8
means all the jobcard refered by 3 or its counter part jobcards vehicles
jobcard_id, vehicle_id
--------------------------
5 3
5 4
8 4
Thank you.
Try to save full path and check it in the next recursion
DECLARE #startVehicleID int=3
;WITH vehCTE AS(
SELECT jobcard_id,vehicle_id,CAST(CONCAT('(',jobcard_id,',',vehicle_id,')') AS varchar(MAX)) [path]
FROM Jobacard_vehicle
WHERE vehicle_id=#startVehicleID
UNION ALL
SELECT v.jobcard_id,v.vehicle_id,c.[path]+CONCAT('(',v.jobcard_id,',',v.vehicle_id,')')
FROM Jobacard_vehicle v
JOIN vehCTE c ON (v.jobcard_id=c.jobcard_id OR v.vehicle_id=c.vehicle_id) AND CHARINDEX(CONCAT('(',v.jobcard_id,',',v.vehicle_id,')'),c.[path])=0
)
SELECT *
FROM vehCTE
ORDER BY [path]
If you need to check Job->Vehicle->Job->Vehicle->... then I think you can use the following
DECLARE #startVehicleID int=3
;WITH vehCTE AS(
SELECT
jobcard_id,
vehicle_id,
CAST(CONCAT('(',jobcard_id,',',vehicle_id,')') AS varchar(MAX)) [path],
1 NextIsJob
FROM Jobacard_vehicle
WHERE vehicle_id=#startVehicleID
UNION ALL
SELECT
v.jobcard_id,
v.vehicle_id,
c.[path]+CONCAT('(',v.jobcard_id,',',v.vehicle_id,')'),
IIF(c.NextIsJob=1,0,1)
FROM Jobacard_vehicle v
JOIN vehCTE c ON ((c.NextIsJob=1 AND v.jobcard_id=c.jobcard_id) OR (c.NextIsJob=0 AND v.vehicle_id=c.vehicle_id)) AND CHARINDEX(CONCAT('(',v.jobcard_id,',',v.vehicle_id,')'),c.[path])=0
)
SELECT *
FROM vehCTE
ORDER BY [path]

Find and classify sequential patterns for a distinct group T-SQL

I need help finding and classifying sequential patterns for each distinct key.
From the data I have, I need to create a new table that contains the key and a pattern identifier that belongs to that key.
From the example below the pattern is as follows:
Key #1 and #3 have the values 1, 2 and 3. The Key #3 has the values 8,
9 and 10. When a distinct pattern exists for a key I.E (1, 2, 3) I
need to create an entry on the table for the key # and that specific
pattern (1, 2, 3)
Data:
key value
1 1
1 2
1 3
2 8
2 9
2 10
3 1
3 2
3 3
Expected Output:
key pattern
1 1
2 2
3 1
Fiddle:
http://sqlfiddle.com/#!6/4fe39
Example table:
CREATE TABLE yourtable
([key] int, [value] int)
;
INSERT INTO yourtable
([key], [value])
VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 8),
(2, 9),
(2, 10),
(3, 1),
(3, 2),
(3, 3)
;
You can concatenate the values together in several ways. The traditional method in SQL Server uses for xml:
select k.key,
stuff( (select ',' + cast(t.id as varchar(255))
from t
where k.key = t.key
for xml path ('')
order by t.id
), 1, 1, ''
) as ids
from (select distinct key from t) k;
You can convert this to a unique number using a CTE/subquery:
with cte as (
select k.key,
stuff( (select ',' + cast(t.id as varchar(255))
from t
where k.key = t.key
for xml path ('')
order by t.id
), 1, 1, ''
) as ids
from (select distinct key from t) k
)
select cte.*, dense_rank() over (order by ids) as ids_id
from cte;

Recursive Teradata Query

I'm trying to query the below table into a consolidated and sorted list, such as:
Beginning list:
GROUP_ID MY_RANK EMP_NAME
1 1 Dan
1 2 Bob
1 4 Chris
1 3 Steve
1 5 Cal
2 1 Britt
2 2 Babs
2 3 Beth
3 1 Vlad
3 3 Eric
3 2 Mike
Query Result:
1 Dan, Bob, Steve, Chris, Cal
2 Britt, Babs, Beth
3 Vlad, Mike, Eric
It needs to use a recursive query because the list is much longer. Also, I have to sort by my_rank to get them in sequential order. Thanks in advance. I've tried about 10 examples found on different forums, but I'm stuck. Also, don't worry about truncating the any trailing/leading commas.
CREATE TABLE MY_TEST (GROUP_ID INTEGER NOT NULL, MY_RANK INTEGER NOT NULL, EMP_NAME VARCHAR(18) NOT NULL);
INSERT INTO MY_TEST VALUES (1, 1, 'Dan');
INSERT INTO MY_TEST VALUES (1, 2, 'Bob');
INSERT INTO MY_TEST VALUES (1, 4, 'Chris');
INSERT INTO MY_TEST VALUES (1, 3, 'Steve');
INSERT INTO MY_TEST VALUES (1, 5, 'Cal');
INSERT INTO MY_TEST VALUES (2, 1, 'Britt');
INSERT INTO MY_TEST VALUES (2, 2, 'Babs');
INSERT INTO MY_TEST VALUES (2, 3, 'Beth');
INSERT INTO MY_TEST VALUES (3, 1, 'Vlad');
INSERT INTO MY_TEST VALUES (3, 3, 'Eric');
INSERT INTO MY_TEST VALUES (3, 2, 'Mike');
What's your Teradata release? Are XML-Services installed?
SELECT * FROM dbc.FunctionsV
WHERE FunctionName = 'XMLAGG';
If this function exists you can avoid recursion (which is not very efficient anyway):
SELECT GROUP_ID,
TRIM(TRAILING ',' FROM
CAST(XMLAGG(EMP_NAME || ',' ORDER BY MY_RANK) AS VARCHAR(10000)))
FROM MY_TEST
GROUP BY 1
Something like this should work:
WITH RECURSIVE employees(Group_ID , Employees, MY_RANK) AS
(
--Recursive Seed
SELECT
GROUP_ID,
--Cast as a big fat varchar so it can hold all of the employees in a list.
CAST(EMP_NAME as VARCHAR(5000)),
--We need to include MY_RANK so we can choose the next highest rank in the recursive term below.
MY_RANK
FROM
MY_TEST
WHERE
--filter for only my_rank = 1 because that's where we want to start
MY_RANK = 1
UNION ALL
--Recursive Term
SELECT
employees.GROUP_ID,
employees.EMP_NAME || ', ' || MY_TEST.EMP_NAME,
MY_TEST.MY_RANK
FROM
employees
INNER JOIN MY_TEST ON
--Joining on Group_id and Rank+1
employees.GROUP_ID = MY_TEST.GROUP_ID AND
employees.MY_RANK + 1 = MY_TEST.MY_RANK
)
SELECT GROUP_ID, Employees FROM employees;

Get all parents for given element and their siblings

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.