Oracle Hierarchical Question (Find step siblings) - oracle

I'm working through a problem where, starting at any 'leaf' or child, I can find that child's siblings and step siblings. To paint a better example, if parent A has two children, and shares one child with parent B, and parent B has two children, one shared with parent A and one shared with parent C, and parent c has two other children not shared with parent B, how do I report all the children? I've tried using recursive with and hierarchical query, but can't seem to get it right. Below is the table structure and values and my attempts thus far.
create table bw_parents(
parent_id char(1) primary key);
create table bw_children(
child_id number(2),
parent_id char(1),
constraint parent_id_fk FOREIGN KEY (parent_id) references bw_parents
);
insert into bw_parents values('A');
insert into bw_parents values('B');
insert into bw_parents values('C');
insert into bw_parents values('D');
insert into bw_parents values('E');
insert into bw_children values(1,'A');
insert into bw_children values(2,'A');
insert into bw_children values(2,'B');
insert into bw_children values(3,'B');
insert into bw_children values(3,'C');
insert into bw_children values(4,'C');
insert into bw_children values(5,'C');
insert into bw_children values(6,'D');
insert into bw_children values(7,'D');
insert into bw_children values(8,'E');
--hierarchical attempt
select A.parent_id, B.child_id, level
from bw_parents A
join bw_children B ON A.parent_id = B.parent_id
start with A.parent_id = 'B'
connect by nocycle prior B.parent_id = B.parent_id
--recursive with
with r as(
select parent_id, null as child_id
from bw_parents
union all
select A.parent_id parent_id, B.child_id child_id
from bw_parents A
join bw_children B ON A.parent_id = B.parent_id)
select *
from r
where parent_id = 'B';

Here is a simple (but perhaps not very performant) solution starting from a given parent:
select distinct child_id
from bw_children
start with parent_id = 'A'
connect by nocycle (mod(level, 2) = 0 and child_id = prior child_id ) or
(mod(level, 2) = 1 and parent_id = prior parent_id)
;
If you must start with a child_id, then you must switch 0 and 1 for the values of mod(level, 2) in the connect by condition; the rest is unchanged.

Related

Insert data into one table from another table avoiding duplicates

I've got a table as follows
Table1
ID Name Tag
-----------------
1 N1 2.1
2 N2 3.5
3 N1 3.5
4 N3 8.1
I create a new table Table2 with ID and Name (unique constraint) and I want to insert Table1's contents into Table2 avoiding duplicates, in the sense that I want only 1, 2 and 4 from Table1 in Table2.
I've tried this but it doesn't seem to work and I get the unique constraint error (ORACLE SQL)
INSERT INTO TABLE2 (ID, NAME)
SELECT ID, NAME
FROM TABLE1
WHERE NAME NOT IN (SELECT NAME FROM TABLE2);
Please can someone point me in the right direction?
Sorry for not making myself clear. Table2 is a brand new table. I want the first values inserted, the following duplicates should be ignored. So in my case, N1, N2 get inserted, N1 is dupe so it is ignored, N3 is inserted
OK - from your description, I understand table t2 is currently empty, and you want to copy the rows where id is in (1, 2, 4) from table t1 to table t2.
Why your code fails:
You seem to believe that the condition is applied to the first row in t1, it passes so it is inserted into t2, then the condition is applied to the second row in t1 (using what is already inserted in t2), etc. - and you don't understand why there is any attempt to insert ALL the rows from t1 into t2. Why doesn't the third row fail the WHERE clause?
Good question! The reason is that operations are done on a SET basis. The WHERE condition uses table t2 AS IT WAS before the INSERT operation began. So for ALL rows, the WHERE clause compares to an empty table t2.
How to fix this... Decide which id you want to add when there are duplicate names. For example, one way to get the result you said you wanted is to select MIN(id) for each name. Moreover, you still want to check if the name exists in t2 already (since you may do this again in the future, when t2 is already partially populated).
insert into t2 ( id, name )
select min(id), name
from t1
where name not in (select name from t2)
group by name
;
You can try it bother....!
Insert into tb2(Field1, Field2)
SELECT Field1, Field2
FROM tb1
WHERE NOT EXISTS (SELECT Field1 FROM tb1) ;
This is how I understood the question:
SQL> create table table2
2 (id number,
3 name varchar2(2),
4 tag number,
5 constraint pk_t2 primary key (id, name)
6 );
Table created.
SQL>
SQL> insert into table2 (id, name, tag)
2 with test (id, name, tag) as
3 (select 1, 'N1', 2.1 from dual union
4 select 2, 'N2', 3.5 from dual union
5 select 3, 'N1', 3.5 from dual union
6 select 4, 'N3', 8.1 from dual
7 )
8 select min(id), name, max(tag)
9 from test
10 group by name;
3 rows created.
SQL>
SQL> select * from table2 order by id;
ID NA TAG
---------- -- ----------
1 N1 3,5
2 N2 3,5
4 N3 8,1
SQL>
When we need to unique any two or more column we have to create unique index.
Run this query
ALTER TABLE TABLE2 ADD UNIQUE unique_index( id, name);
and then
INSERT INTO TABLE2 (id,name,tag) VALUES(1, "N1", 3.5 )
ON DUPLICATE KEY UPDATE tag=3.5
this will also help to update new tag
Try to check if the id and name from Table1 is doesn't exist in Table2, if then insert.
If the unique constraint on TABLE2 is a composite key then run this:
INSERT INTO TABLE2 (ID, NAME)
SELECT A.ID, A.NAME
FROM TABLE1 A
WHERE NOT EXISTS (SELECT NULL FROM TABLE2 B WHERE A.ID=B.ID AND A.NAME=B.NAME);
If there are two unique constraints; one on the id, and the other on the name then run this instead:
INSERT INTO TABLE2 (ID, NAME)
SELECT A.ID, A.NAME
FROM TABLE1 A
WHERE NOT EXISTS (SELECT NULL FROM TABLE2 B WHERE A.ID=B.ID OR A.NAME=B.NAME);
ORACLE, in case you need to get values from 2 different tables.
below example,i use an increment case.
INSERT INTO TABLE1
(INDEX, REMARKS, NAME, AGE)
(SELECT (SELECT colescs(MAX(INDEX),0) FROM TABLE1)+1,
'any remarks',
t2.NAME, t2,age from TABLE2 t2 where t2.name = 'apple')
explanation
match below numbers (1)-(1), (2)-(2) ...
INSERT INTO TABLE1
(INDEX, //index increment (1)
REMARKS, //hard code (2)
NAME, //from table2 (3)
AGE) //from table2 (4)
(SELECT // this part is to get values from another table
(SELECT colescs(MAX(INDEX),0) FROM TABLE1)+1, //increment (1)
'any remarks', //hard code value (2)
t2.NAME, //from table2 (3)
t2,age //from table2 (4)
from TABLE2 t2 where t2.name = 'apple') //condition for table2

Delete data returned from subquery in oracle

I have two tables. if the data in table1 is more than a predefined limit (say 2), i need to copy the remaining contents of table1 to table2 and delete those same contents from table1.
I used the below query to insert the excess data from table1 to table2.
insert into table2
SELECT * FROM table1 WHERE ROWNUM < ((select count(*) from table1)-2);
Now i need the delete query to delete the above contents from table1.
Thanks in advance.
A straightforward approach would be an interim storage in a temporary table. Its content can be used to determine the data to be deleted from table1 as well as the source to feed table 2.
Assume (slightly abusing notation) to be the PK column (or that of any candidate key) of table1 - usually there'll be some key that comprises only 1 column.
create global temporary table t_interim as
( SELECT <pk> pkc FROM table1 WHERE ROWNUM < ((select count(*) from table1)-2 )
;
insert into table2
select * from table1 where <pk> IN (
select pkc from t_interim
);
delete from table1 where <pk> IN (
select pkc from t_interim
);
Alternative
If any key of table1 spans more than 1 column, use an EXISTS clause instead as follows ( denoting the i-th component of a candidate key in table1):
create global temporary table t_interim as
( SELECT <ck_1> ck1, <ck_2> ck2, ..., <ck_n> ckn FROM table1 WHERE ROWNUM < ((select count(*) from table1)-2 )
;
insert into table2
select * from table1 t
where exists (
select 1
from t_interim t_i
where t.ck_1 = t_i.ck1
and t.ck_2 = t_i.ck2
...
and t.ck_n = t_i.ckn
)
;
delete from table1 t where
where exists (
select 1
from t_interim t_i
where t.ck_1 = t_i.ck1
and t.ck_2 = t_i.ck2
...
and t.ck_n = t_i.ckn
)
;
(Technically you could try to adjust the first scheme by synthesizing a key from the components of any CK, eg. by concatenating. You run the risk of introducing ambiguities ( (a bc, ab c) -> (abc, abc) ) or run into implementation limits ( max. varchar length ) using the first method)
Note
In case the table doesn't have a PK, you can apply the technique using any candidate key of table1. There will always be one, in the extreme case it's the set of all columns.
This situation may be the right time to improve the db design and add a (synthetic) pk column to table1 ( and any other tables in the system that lack it).

Copy rows from one table to another, ignoring duplicates on remote

I have two table with the same columns in different databases. Both table have records.i want to insert the records of table2 in table1 but i want to ignore those records which are already in table 1. As well i want to store all ignored records in a new table.
Example:
create table dest
(id number primary key,
col1 varchar2(10));
create table src
(id number,
col1 varchar2(10));
insert into src values(1,'ABC');
insert into src values(2,'GHB');
insert into src values(3,'DUP');
insert into src values(3,'DUP');
commit;
merge into dest
using
(select id,col1 from src) src on(dest.id=src.id)
when not matched then
insert values(src.id,src.col1)
when matched
then update set dest.col1=src.col1;
Error report -
SQL Error: ORA-00001: unique constraint (SCOTT.SYS_C0010807) violated
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.
you can use intersect and minus to determine the differences
-- Test Data
-- table1#abc
with data1(id,
val) as
(select 1, 'val1'
from dual
union all
select 2, 'val2'
from dual
union all
select 3, 'val3'
from dual),
-- table2#xyz
data2(id,
val) as
(select 1, 'another val1'
from dual
union all
select 2, 'val2'
from dual
union all
select 4, 'val4'
from dual)
-- Intersection
select 'Intersection', intersection.*
from ((select * from data2) intersect (select * from data1)) intersection
union all
-- data2 not in data1
select 'data2 not in data1', d2.*
from ((select * from data2) minus (select * from data1)) d2
union all
-- data1 not in data2
select 'data1 not in datad', d1.*
from ((select * from data1) minus (select * from data2)) d1;

connect by prior giving incorrect results

Assume I have 2 tables as shown below – PARENT and CHILD
create table parent
(
parent_id number,
parent_name varchar2(10),
cnt number
)
create table children
(
child_id number,
parent_id number,
name varchar2(10)
)
insert into parent values (1, 'A', 3);
insert into parent values (2, 'B', 2);
insert into children values (1, 1, 'AB');
insert into children values (1, 2, 'AC');
insert into children values (1, 3, 'AD');
insert into children values (2, 1, 'BA');
insert into children values (2, 2, 'BB');
The output has to be something like below:
Parent_ID Parent_Name Cnt Child_Names
1 A 3 AB, AC, AD
2 B 2 BA, BB
I have written the below query to achieve this. I don't know where it is going wrong, query seems to be fine but the output is not what is desired.
Please help me out as I am almost saturated debugging this.
select parent_id, parent_name, substr(max(sys_connect_by_path(child_name, ',')),2)
from
( select p.parent_id, p.parent_name, ch.child_name, row_number() over (partition by p.parent_id, p.parent_name order by ch.child_name) rn
from parent p, children ch
where p.parent_id = ch.parent_id
)
start with rn =1
connect by prior rn+1 = rn
group by parent_id, parent_name
You don't see hierartical data. It is simple master detail.
select p.parent_id, p.parent_name, p.cnt, c.children_names, c.real_count
from parent p left outer join
(select parent_id, listagg(name, ', ') within group (order by name) children_names,
count(*) real_count from children group by parent_id) c
on p.parent_id = c.parent_id
'AD' is not connected because it parent_id is 3.
Hm... listagg is a 11g feature.
The general solution before 11g was to use Tom Kyte's aproach:
http://www.sqlsnippets.com/en/topic-11591.html
And have a look at http://www.oracle-developer.net/display.php?id=515 for performance expectations
Ok, let's continue with "connect by" aproach.
First of all, lets reduce complexity by forgetting about parent table. We will be building right side of left outer join query above, but using connect by construction.
Second, I think you missed how parent_id and child_id are named. My guess is that your child_id is actually your parent_id in parent table. So name is wrong.
3 conditions to put all children in a row:
All children have the same child_id (parent.parent_id)
first one has parent_id = 1 (more like order_id)
every next one has parent_id +1 comparing to previous one
Let's put it all into query:
select c.*, level from children c
start with parent_id = 1 --2nd condition
connect by prior
c.parent_id = c.parent_id -1 --3rd condition
and prior child_id = child_id --1st condition
order by child_id, parent_id
Then you add your SYS_CONNECT_BY_PATH(name, ', '), get the last element and only then join it with parent table, but based on parent.parent_id = children.child_id
here is your query after correction, actually you did some mistake in column name of children table(wrote ch.child_name while it is ch.name or name)
select parent_id, parent_name,cnt,substr(max(sys_connect_by_path(child_name, ',')),2) child_name
from
( select p.parent_id, p.parent_name, p.cnt , ch.name child_name, row_number() over (partition by p.parent_id, p.parent_name order by ch.name) rn
from parent p, children ch
where p.parent_id = ch.parent_id
)
start with rn =1
connect by prior rn+1 = rn
group by parent_name,cnt,parent_id

Comparision in the tree in hierarchical queries

I have a requirement to pull the data in the below fashion
Compare_Result Association_Type_name Association_Role
Type in Dev,but not Prod ProcessingSite Null
Role in Dev,but not Prod ProcessingSite MoodedActivity
Role in Dev,but not Prod Specimen ProductRelationship
Requirement: I need to compare the development and production. if any association_type or association_role are not exists in production then i need to return a message like "the Type Object found in Development,but not Production"
Could any one please give me an idea.
sample data
create table nodes
(
node_id number(5),
parent_node_id number(5),
object_id number(5)
);
begin
insert into nodes values(4001,7001,2001);
insert into nodes values(4002,4001,2005);
insert into nodes values(4003,4002,2002);
insert into nodes values(4004,4003,2004);
insert into nodes values(4005,4004,2003);
insert into nodes values(4006,4004,2006);
insert into nodes values(4007,4004,2007);
insert into nodes values(4008,4003,2008);
insert into nodes values(4009,4008,2011);
insert into nodes values(4010,4008,2013);
insert into nodes values(4011,4008,2014);
insert into nodes values(4012,4003,2009);
insert into nodes values(4013,4012,2015);
insert into nodes values(4014,4012,2017);
insert into nodes values(4015,4001,2020);
insert into nodes values(4016,4015,2012);
insert into nodes values(4017,4016,2016);
insert into nodes values(4018,4017,2024);
insert into nodes values(4019,4017,2023);
insert into nodes values(4020,4016,2029);
insert into nodes values(4021,4020,2022);
insert into nodes values(4022,4020,2021);
insert into nodes values(4023,4020,2019);
insert into nodes values(4024,4016,2010);
insert into nodes values(4025,4024,2018);
end;
/
create table objects
(
object_id number(5),
object_type_id number(5),
name varchar2(40)
);
begin
insert into objects values ( 2001,5001,'iad_Dictionary_area');
insert into objects values ( 2002,5003,'iad_Association_Dictionary');
insert into objects values ( 2003,5005,'TreatingSite');
insert into objects values ( 2004,5004,'ProcessingSite');
insert into objects values ( 2005,5002,'Development');
insert into objects values ( 2006,5005,'MoodedActivity');
insert into objects values ( 2007,5005,'MaterialName');
insert into objects values ( 2008,5004,'PerformedClass');
insert into objects values ( 2009,5004,'Specimen');
insert into objects values ( 2010,5004,'Specimen');
insert into objects values ( 2011,5005,'RegulatoryAssessment');
insert into objects values ( 2012,5003,'iad_Association_Dictionary');
insert into objects values ( 2013,5005,'Submission');
insert into objects values ( 2014,5005,'Mooded');
insert into objects values ( 2015,5005,'class13th');
insert into objects values ( 2016,5004,'ProcessingSite');
insert into objects values ( 2017,5005,'ProductRelationship');
insert into objects values ( 2018,5005,'class13th');
insert into objects values ( 2019,5005,'Distributor');
insert into objects values ( 2020,5006,'Production');
insert into objects values ( 2021,5005,'Assessor');
insert into objects values ( 2022,5005,'StudyInvestigator');
insert into objects values ( 2023,5005,'MaterialName');
insert into objects values ( 2024,5005,'TreatingSite');
insert into objects values ( 2029,5004,'BiologicEntityGroup');
end;
/
create table object_types
(
object_type_id number(5),
name varchar2(35)
);
begin
insert into object_types values (5001,'Dictionary_area');
insert into object_types values (5002,'Development');
insert into object_types values (5003,'Association_Dictionary');
insert into object_types values (5004,'Association_Type');
insert into object_types values (5005,'Association_Role');
insert into object_types values (5006,'Production');
end;
/
example
let us assume i have below structure
Dictionary_area
development
Association_Dictionary
ProcessingSite(type1)
TreatingSite(role1)
MoodedActivity(role2)
MaterialName(role3)
PerformedClass(type2)
RegulatoryAssessment(role1)
Submission(role2)
Mooded(role3)
Specimen(type3)
class13th(role1)
ProductRelationship(role2)
production
Association_Dictionary
ProcessingSite(type1)
TreatingSite(role1)
MaterialName(role2)
BiologicEntityGroup(type2)
StudyInvestigator(role1)
Assessor(role2)
Distributor(role3)
Specimen (type3)
class13th(role1)
ProcessingSite is an association_type and it is having 3 association_roles. we can observe same association_type in the production, but MoodedActivity(association_role) is not available. in this case we need to return "Type Object found in Development,but not Production".
Tables information
nodes: this table will be having nodes and parent nodes information. for example if development object is having some node like 100 and its parent node is 99.
objects: objects information.
object_types: object_type information. for example ProcessingSite object_type is "Association_Type".
i tried in the below fashion
WITH childs AS
(SELECT n.object_id, n.node_id, n.parent_node_id, ot.NAME, "path",
rootnode
FROM (SELECT object_id, node_id, parent_node_id,
SYS_CONNECT_BY_PATH (object_id, '/') "path",
CONNECT_BY_ROOT (object_id) rootnode
FROM nodes
START WITH object_id = 2001
CONNECT BY PRIOR node_id = parent_node_id) n
JOIN
objects o ON n.object_id = o.object_id
JOIN object_types ot ON o.object_type_id = ot.object_type_id
),
devobj AS
(SELECT n.object_id, n.NAME
FROM (SELECT *
FROM childs) n
WHERE n.NAME = 'Development'),
prodobj AS
(SELECT n.object_id, n.NAME
FROM (SELECT *
FROM childs) n
WHERE n.NAME = 'Production'),
dev_associ_type_obj AS
(SELECT *
FROM (SELECT object_id, NAME
FROM (SELECT *
FROM childs)
START WITH object_id = (SELECT object_id
FROM devobj)
CONNECT BY PRIOR node_id = parent_node_id) t
WHERE t.NAME = 'Association_Type'),
dev_associ_roles_obj AS
(SELECT object_id, NAME, CONNECT_BY_ROOT (object_id) rootnode
FROM (SELECT *
FROM childs)
START WITH object_id IN (SELECT object_id
FROM dev_associ_type_obj)
CONNECT BY PRIOR node_id = parent_node_id),
prod_associ_type_obj AS
(SELECT *
FROM (SELECT object_id, NAME
FROM (SELECT *
FROM childs)
START WITH object_id = (SELECT object_id
FROM devobj)
CONNECT BY PRIOR node_id = parent_node_id) t
WHERE t.NAME = 'Association_Type'),
prod_associ_roles_obj AS
(SELECT object_id, NAME, CONNECT_BY_ROOT (object_id) rootnode
FROM (SELECT *
FROM childs)
START WITH object_id IN (SELECT object_id
FROM prod_associ_type_obj)
CONNECT BY PRIOR node_id = parent_node_id)
SELECT *
FROM prod_associ_roles_obj
MINUS
SELECT *
FROM dev_associ_roles_obj

Resources