Delete rows from table which does not exists in other table by picking eligible records only - oracle

I am using Oracle. i need to delete the rows from One table which does not exists in other table by joining it with table having only eligible ID.
I am sorry i am not sure how to explain it better. Below is an example. ID + SUB_ID is PK
Final_table -
ID SUB_ID Name
1 1 Football
1 2 Cricket
1 3 Formula1
1 4 Tennis
2 1 Hockey
2 2 Archery
2 3 Badminton
3 1 Basketball
3 2 Dodgeball
Latest_Table
ID SUB_ID Name
1 1 Football
1 2 Cricket
1 3 Formula1
2 1 Hockey
2 2 Archery
3 1 Basketball
Sample_Table
ID
1
3
From above example, i joined Latest_table with Sample_Table on ID column and it gave me ID (1,3). Now based on these IDs which are eligible, i want to delete the rows from Final_Table which are not present in Latest_Table. I don't want it to delete anything for ID=2 as it is not eligible.
I have written below code, but it is deleting everything from FINAL_TABLE which is not present in Latest_table.
Delete from FINAL_TABLE FT
where not exists
(select 1 from
Latest_table LT, Sample_table ST
where LT.ID = ST.ID
and LT.ID = FT.ID
and LT.SUB_ID = FT.SUB_ID);
Thank you for your help.
Edited-
desired result in Final_Table should look like
Final_table -
ID SUB_ID Name
1 1 Football
1 2 Cricket
1 3 Formula1
2 1 Hockey
2 2 Archery
2 3 Badminton
3 1 Basketball

Edit: I misread your query originally. I have changed my answer to remove the join to Sample_Table in the first condition.
Delete from FINAL_TABLE FT
where not exists
(select 1 from
Latest_table LT
where LT.ID = FT.ID
and LT.NAME = FT.NAME
and LT.SUB_ID = FT.SUB_ID)
AND FT.id IN
(
SELECT id FROM Sample_Table
)
This will only delete from Final_Table if the id appears in Sample_Table and the record (all 3 columns) does not appear in the Latest_table.
An alternative way of writing this is
Delete from FINAL_TABLE FT
where
(FT.ID, FT.SUB_ID, FT.NAME) NOT IN
(SELECT LT.ID, LT.SUB_ID, LT.NAME FROM Latest_table LT)
AND FT.ID IN
(SELECT ST.id FROM Sample_Table ST)

Related

how to convert column value to multiple insert rown oracle cursor

I am trying to copy value from our old db to new db where there is a change in the table structure.
Below is the structure of the table
Table1
Table1ID, WheelCount, BlindCount, OtherCount
For eg values of table 1 is like below
Table1ID, 1,2,5
Table1 is now changed to TableNew with the below
TableNewID, DisableID , Quantity.
So the value should be
TableNewID1, 1,1 here 1= WheelCount from table1
TableNewID2, 2,2 here 2= BlindCount
TableNewID3, 3,5 here 5= OtherCount
how to write a cursor to transform table1 value to the new table tableNew structure.
Table1
Table1ID WheelCount BlindCount OtherCount
1 1 2 5
2 8 10 15
A master table defined to map disableid
DisableID Type
1 wheelCount
2 blindcount
3 otherCount
Expected structure
ID **Table1ID** **DISABLEID** QUANTITY
1 1 1 1
2 1 2 2
3 1 3 5
4 2 1 8
5 2 2 10
6 2 3 15
The simplest is a UNION ALL for each column you want to turn into a row.
insert into tablenew
select table1id,1,wheelcount from table1
union all
select table1id,2,blindcount from table1
union all
select table1id,3,othercount from table1
There are other, sleeker methods for avoiding multiple passes on the first table, in case it's huge.
This is how I understood it.
Current table contents:
SQL> SELECT * FROM table1;
TABLE1ID WHEELCOUNT BLINDCOUNT OTHERCOUNT
---------- ---------- ---------- ----------
1 1 2 5
2 8 10 15
Prepare new table:
SQL> CREATE TABLE tablenew
2 (
3 id NUMBER,
4 table1id NUMBER,
5 disableid NUMBER,
6 quantity NUMBER
7 );
Table created.
Sequence (will be used to populate tablenew.id column):
SQL> CREATE SEQUENCE seq_dis;
Sequence created.
Trigger (which actually populates tablenew.id):
SQL> CREATE OR REPLACE TRIGGER trg_bi_tn
2 BEFORE INSERT
3 ON tablenew
4 FOR EACH ROW
5 BEGIN
6 :new.id := seq_dis.NEXTVAL;
7 END;
8 /
Trigger created.
Copy data:
SQL> INSERT INTO tablenew (table1id, disableid, quantity)
2 SELECT table1id, 1 disableid, wheelcount AS quantity FROM table1
3 UNION ALL
4 SELECT table1id, 2 disableid, blindcount AS quantity FROM table1
5 UNION ALL
6 SELECT table1id, 3 disableid, othercount AS quantity FROM table1;
6 rows created.
Result:
SQL> SELECT *
2 FROM tablenew
3 ORDER BY table1id, disableid;
ID TABLE1ID DISABLEID QUANTITY
---------- ---------- ---------- ----------
1 1 1 1
3 1 2 2
5 1 3 5
2 2 1 8
4 2 2 10
6 2 3 15
6 rows selected.

How to join in single row result with one row and multiple rows in another table?

I have 2 tables, tables 1 is tree structure.
Table 1:
ID NAME PARENTID
-------------------------
1 Book1 0
2 Page 1
3 Line1 2
4 Line2 2
5 Book2 0
6 Page1 5
7 Page2 5
8 Line1 6
9 Line2 6
Table 2:
ID BOOK PAGE LINE
1 1 2 4
2 5 7 9
I want to get all of rows in table 2 and show the Name of table 1 in one line
ID BOOK PAGE LINE
1 Book1 Page Line2
2 Book2 Page2 Line2
How can I show the data from multiple rows and 1 row in single row not duplicate if I use simple select. Sorry for about the stupid problem. Thanks for your help.
Use joins (tbl is second table):
select t1.id,
t2.name book,
t3.name page,
t4.name line
from tbl t1
join treeTbl t2
on t1.book = t2.id
join treeTbl t3
on t3.id=t1.page
join treeTbl t4
on t4.id=t1.line

Inactivate duplicate record and re-point child records to active one

There are two table as below
Table1
ID Name Age Active PID
-----------------------------
1 A 2 Y 100
2 A 2 Y 100
3 A 2 Y 100
4 B 3 Y 200
5 B 3 Y 200
Table2
T2ID CID
---------
10 1
20 1
30 1
40 2
50 2
60 3
70 3
80 3
90 4
100 5
110 5
I am trying to inactivate the duplicate record of table 1 and reassign the table2 record to activated rows of table 1,The result for table1 and table2 should be as below
ID Name Age Active PID
-----------------------------
1 A 2 Y 100
2 A 2 N 100
3 A 2 N 100
4 B 3 N 200
5 B 3 Y 200
T2ID CID
---------
10 1
20 1
30 1
40 1
50 1
60 1
70 1
80 1
90 5
100 5
110 5
please help for oracle query to update
You can do this by using two merge statements, like so:
Update table2:
MERGE INTO table2 tgt
USING (WITH t1 AS (SELECT ID,
NAME,
age,
active,
pid,
MIN(ID) OVER (PARTITION BY pid) min_id,
CASE WHEN COUNT(CASE WHEN active = 'Y' THEN 1 END) OVER (PARTITION BY pid) > 1 THEN 'Y' ELSE 'N' END multi_active_rows
FROM table1)
SELECT t2.t2id,
t2.cid old_cid,
t1.min_id new_cid
FROM t1
INNER JOIN table2 t2 ON t1.id = t2.cid
WHERE t1.multi_active_rows = 'Y') src
ON (tgt.t2id = src.t2id)
WHEN MATCHED THEN
UPDATE SET tgt.cid = src.new_cid;
Update table1:
MERGE INTO table1 tgt
USING (WITH t1 AS (SELECT ID,
NAME,
age,
active,
pid,
MIN(ID) OVER (PARTITION BY pid) min_id,
CASE WHEN COUNT(CASE WHEN active = 'Y' THEN 1 END) OVER (PARTITION BY pid) > 1 THEN 'Y' ELSE 'N' END multi_active_rows
FROM table1)
SELECT ID
FROM t1
WHERE multi_active_rows = 'Y'
AND ID != min_id) src
ON (tgt.id = src.id)
WHEN MATCHED THEN
UPDATE SET active = 'N';
Since we want to derive the results to update both table1 and table2 from the original dataset in table1, it's easier to update table2 first before updating table1.
This works by finding the lowest id across each set of pids in table1, plus checking to see if there is more than one active row for each pid (there's no need to do any updates if we have at most one active row available).
Once we have that information, we can use that to decide which rows to update in each table, and we can use the min_id to update table2 with, and we can update any rows in table1 where the id doesn't match the min_id to be not active.
N.B. If you could have a mix of Ys and Ns in your data, you may need to skip the and id != min_id check in the second merge statement and amend the update part to update the row to Y if the id is the min_id, otherwise set it to N.

How do you shift values down in a column in an Oracle table?

Given the following oracle database table:
group revision comment
1 1 1
1 2 2
1 null null
2 1 1
2 2 2
2 3 3
2 4 4
2 null null
3 1 1
3 2 2
3 3 3
3 null null
I want to shift the comment column one step down in relation to version, within its group, so that I get the following table:
group revision comment
1 1 null
1 2 1
1 null 2
2 1 null
2 2 1
2 3 2
2 4 3
2 null 4
3 1 null
3 2 1
3 3 2
3 null 3
I have the following query:
MERGE INTO example_table t1
USING example_table t2
ON (
(t1.revision = t2.revision+1 OR
(t2.revision = (
SELECT MAX(t3.revision)
FROM example_table t3
WHERE t3.group = t1.group
) AND t1.revision IS NULL)
)
AND t1.group = t2.group)
WHEN MATCHED THEN UPDATE SET t1.comment = t2.comment;
That does most of this (still need a separate query to cover revision = 1), but it is very slow.
So my question is, how do I use Max here as efficiently as possible to pull out the highest revision for each group?
I would use lag not max
create table example_table(group_id number, revision number, comments varchar2(40));
insert into example_table values (1,1,1);
insert into example_table values (1,2,2);
insert into example_table values (1,3,null);
insert into example_table values (2,1,1);
insert into example_table values (2,2,2);
insert into example_table values (2,3,3);
insert into example_table values (2,4,null);
select * from example_table;
merge into example_table e
using (select group_id, revision, comments, lag(comments, 1) over (partition by group_id order by revision nulls last) comments1 from example_table) u
on (u.group_id = e.group_id and nvl(u.revision,0) = nvl(e.revision,0))
when matched then update set comments = u.comments1;
select * from example_table;

Updating status in 1 table, based on most recent response in another table

I'm using Oracle 11g R1 database. Please help me with what I'm trying to achive.
Table 1
-------
ID Name Status
-- ---- ------
1 John 0
2 Chris 0
3 Joel 0
4 Mike 0
5 Henry 0
Table 2
-------
ID Status ResponseDate
-- ------ -------------
1 0 1-Jan-2013
1 1 31-Jan-2013
1 2 3-Feb-2013
1 6 19-Jan-2013
2 6 3-Mar-2013
2 2 1-Mar-2013
2 1 4-Mar-2013
2 0 2-Mar-2013
3 0 3-Feb-2013
3 1 2-Feb-2013
3 2 1-Feb-2013
4 2 4-Apr-2013
4 1 6-Apr-2013
4 0 1-Apr-2013
5 1 31-Mar-2013
5 6 4-Apr-2013
5 3 10-Jan-2013
I would like to update Table1.status based on the most recent response the ID's have returned. So, the statuses in Table1 should finally be updated as below,
ID Name Status
-- ---- ------
1 John 2
2 Chris 1
3 Joel 0
4 Mike 1
5 Henry 6
update table1 t1
set status = (
select max(status) keep (dense_rank last order by responsedate)
from table2 t2
where t2.id = t1.id
);
update table1 t1
set status =
(
select status
from table2
where id = t1.id and responseDate =
(
select max(responseDate)
from table2
where id = t1.id
)
)
Of course you can update status column of the table1 every time a need arises, but you might consider to create a view, v_table_1 for instance, which will provide you with fresh and up to date information:
create or replace view V_Table1 as
select max(t.id) as id
, max(t.name) as name
, max(q.status) keep(dense_rank first
order by q.ResponseDate desc) as status
from table_1 t
join table_2 q
on (q.id = t.id)
group by t.id
Result:
select *
from V_Table1
ID1 NAME1 STATUS
-------- ----- ----------
1 John 2
2 Chris 1
3 Joel 0
4 Mike 1
5 Henry 6

Resources