Comparision in the tree in hierarchical queries - oracle

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

Related

Avoid using same view multiple times inside oracle procedures

I am using a view named V_ENT_MSG in my oracle procedure "TEST PROC" as shown below. The same view is used in multiple places inside the procedure and the procedure execution time is very high. The view returns more than 5 Million records every time. How can I improve procedure execution time. [ Note that I cannot change the view V_ENT_MSG].
create or replace PROCEDURE "TESTPROC"
AS
BEGIN
INSERT
INTO OUTBOUND1
(
DELIVERY_EVENT_ID,
MESSAGE_ID
)
SELECT
V2.DELIVERY_EVENT_ID,PS.MESSAGE_ID
FROM V_ENT_MSG V2,
R_SOURCE PS
WHERE V2.primary_source = PS.SOURCE;
COMMIT;
INSERT
INTO OUTBOUND2
(
DELIVERY_EVENT_ID,
MESSAGE_BODY
)
SELECT
V2.DELIVERY_EVENT_ID,PS2.MESSAGE_BODY
FROM V_ENT_MSG V2,
R_SOURCE2 PS2
WHERE V2.primary_source = PS2.SOURCE;
COMMIT;
INSERT
INTO OUTBOUND3
(
DELIVERY_EVENT_ID,
SUBJECT
)
SELECT
V2.DELIVERY_EVENT_ID,PS3.SUBJECT
FROM V_ENT_MSG V2,
R_SOURCE3 PS3
WHERE V2.primary_source = PS3.SOURCE;
COMMIT;
END TESTPROC;
You may be able to rewrite this as a multitable insert. To do this, you need to:
(Outer) join the view to each table
insert all the result of this query
Use the when clause to control which rows go in which tables
Which looks something like:
insert all
when ps.source is not null then
into outbound1 ( delivery_event_id, message_id )
values ( delivery_event_id, message_id )
when ps2.source is not null then
into outbound2 ( delivery_event_id, message_body )
values ( delivery_event_id, message_body )
when ps3.source is not null then
into outbound3 ( delivery_event_id, subject )
values ( delivery_event_id, subject )
select v.delivery_event_id,ps3.subject
from v_ent_msg v
left join r_source ps
on v.primary_source = ps.source
left join r_source2 ps2
on v.primary_source = ps2.source
left join r_source3 ps3
on v.primary_source = ps3.source;
This assumes that each primary_source joins to at most one row in r_sourceX. If it's 1:M and each value of primary_source can join to a different number of rows in each of the other tables it gets trickier.
You'll need to assign a row_number for each source value in each r_sourceX table and only insert those rows where this is one.
Additionally whether you have opted for GTT as suggested by #Littlefoot or not ,you can still use this approach.
How about leveraging INSERT ALL with WITH clause to achieve the same ,
INSERT ALL
/* inserting to OUTBOUND1 based on table_name = R_SOURCE defined during union */
WHEN table_name = 'R_SOURCE' THEN
INTO outbound1(delivery_event_id, message_id)
VALUES(delivery_event_id, message_type)
/* inserting to OUTBOUND2 based on table_name = R_SOURCE2 defined during union */
WHEN table_name = 'R_SOURCE2' THEN
INTO outbound2(delivery_event_id, message_body)
VALUES (delivery_event_id, message_type)
/* inserting to OUTBOUND3 based on table_name = R_SOURC3 defined during union */
WHEN table_name = 'R_SOURCE3' THEN
INTO outbound3(delivery_event_id, subject)
VALUES (delivery_event_id, message_type)
WITH ent_msg
AS
(SELECT v2.delivery_event_id,v2.primary_source
FROM v_ent_msg v2),
src_data
AS
( SELECT v2.delivery_event_id delivery_event_id,ps.message_id message_type,'R_SOURCE' table_name
FROM r_source ps
JOIN ent_msg v2
ON v2.primary_source = ps.source
UNION ALL
SELECT v2.delivery_event_id,ps2.message_body,'R_SOURCE2' table_name
FROM r_source2 ps2
JOIN ent_msg v2
ON v2.primary_source = ps2.source
UNION ALL
SELECT v2.delivery_event_id,ps3.subject,'R_SOURCE3' table_name
FROM r_source3 ps3
JOIN ent_msg v2
ON v2.primary_source = ps3.source
)
SELECT delivery_event_id,message_type,table_name
FROM src_data
I couldn't validate SQL because of tables are missing.
There are other ways too in case of performance issue where we can go for BULK COLLECT with LIMIT. I am not aware of the data set you are dealing with.
Hope this guides you proceeding with your question.

Oracle Hierarchical Question (Find step siblings)

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.

insert all and inner join in oracle

I would like to insert data in to two tables. Will be one-to-many connection. For this, I have to use Foreign Key, of course.
I think, table1 - ID column is an ideal for this a Primary Key. But I generate it always with a trigger, automatically, every line. SO,
How can I put Table1.ID (auto generated, Primary Key) column in to table2.Fkey column in the same insert query?
INSERT ALL INTO table1 ( --here (before this) generated the table1.id column automatically with a trigger.
table1.food,
table1.drink,
table1.shoe
) VALUES (
'apple',
'water',
'slippers'
)
INTO table2 (
fkey,
color
) VALUES (
table1.id, -- I would like table2.fkey == table1.id this gave me error
'blue'
) SELECT
*
FROM
table1
INNER JOIN table2 ON table1.id = table2.fkey;
The error message:
"00904. 00000 - "%s: invalid identifier""
As suggested by #OldProgrammer, use sequence
INSERT ALL INTO table1 ( --here (before this) generated the table1.id column automatically with a trigger.
table1_id,
table1.food,
table1.drink,
table1.shoe
) VALUES (
<sequecename_table1>.nextval,
'apple',
'water',
'slippers'
)
INTO table2 (
fkey,
color
) VALUES (
<sequecename_table2>.nextval,
<sequecename_table1>.currval, -- returns the current value of a sequence.
'blue'
) SELECT
*
FROM
table1
INNER JOIN table2 ON table1.id = table2.fkey;
Since you're using Oracle DB's 12c version, then might use Identity Column Property. Then easily return the value of first table's (table1) to a local variable by charging of returning clause just after an insert statement for table1, and use inside the next insert statement which is for table2 as stated below :
SQL> create table table1(
2 ID integer generated always as identity primary key,
3 food varchar2(50), drink varchar2(50), shoe varchar2(50)
4 );
SQL> create table table2(
2 fkey integer references table1(ID),
3 color varchar2(50)
4 );
SQL> declare
2 cl_tab table1.id%type;
3 begin
4 insert into table1(food,drink,shoe) values('apple','water','slippers' )
5 returning id into cl_tab;
6 insert into table2 values(cl_tab,'blue');
7 end;
8 /
SQL> select * from table1;
ID FOOD DRINK SHOE
-- ------- ------- -------
1 apple water slippers
SQL> select * from table2;
FKEY COLOR
---- --------------------------------------------------
1 blue
Anytime you issue the above statement for insertions between begin and end, both table1.ID and table2.fkey columns will be populated by the same integer values. By the way do not forget to commit the changes by insertions, if you need these values throughout the DB(i.e.from other sessions also).

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).

select values from table and create oracle objects

i want to insert values to oracle Object type by selecting values from other table
And the tables and insert statement looks like this.
CREATE TYPE Test_obj AS OBJECT (
attr1 VARCHAR2(20),
attr2 VARCHAR2(20),
attr3 VARCHAR2(25) );
/
CREATE TABLE resultrow_obj (
resultrow Test_obj ,
RESULTTABLEID NUMBER(20,0),
ROWNUMBER NUMBER(20,0) );
/
INSERT INTO resultrow_obj VALUES (
Test_obj (select col1,col2,col3 from Table2 where rownum<=1),
1,123 );
/
You've got it nearly right:
SQL> INSERT INTO resultrow_obj
2 VALUES((SELECT Test_obj('A', 'B', 'C')
3 FROM dual WHERE rownum <= 1),
4 1, 123);
1 row inserted

Resources