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

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

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.

Oracle - how to insert if not exists?

Example dB : https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=49af209811bce88aa67b42387f1bb5f6
I'd like to add insert this line
1002 9 1 UNKNOWN
Because of the line exists
1002 5 1 JIM
I was thinking about something like
select codeclient from STATS_CLIENT_TEST where CODEAXESTAT=5
and insert codeclient, 9,1,UNKNOWN.
but not sure how to do it? And simple query or a PL/SQL?
What's the best way to get it?
Thanks
Use an INSERT .. SELECT statement with a PARTITIONed outer join:
INSERT INTO stats_client_test (
codeclient, codeaxestat, codeelementstat, valeuraxestatistiqueclient
)
SELECT cc.codeclient,
s.codeaxestat,
s.codeelementstat,
'UNKNOWN'
FROM (SELECT DISTINCT codeclient FROM stats_client_test) cc
LEFT OUTER JOIN stats_client_test s
PARTITION BY (s.codeaxestat, s.codeelementstat)
ON (s.codeclient = cc.codeclient)
WHERE s.rowid IS NULL;
or a MERGE statement:
MERGE INTO stats_client_test dst
USING (
SELECT cc.codeclient,
s.codeaxestat,
s.codeelementstat,
s.ROWID AS rid
FROM (SELECT DISTINCT codeclient FROM stats_client_test) cc
LEFT OUTER JOIN stats_client_test s
PARTITION BY (s.codeaxestat, s.codeelementstat)
ON (s.codeclient = cc.codeclient)
) src
ON (dst.ROWID = src.rid)
WHEN NOT MATCHED THEN
INSERT (codeclient, codeaxestat, codeelementstat, valeuraxestatistiqueclient)
VALUES (src.codeclient, src.codeaxestat, src.codeelementstat, 'UNKNOWN');
db<>fiddle here
Here's one option: using the MINUS set operator, find missing codeclient values and then insert appropriate row(s).
Before:
SQL> select * From stats_client_Test order by codeaxestat, codeclient;
CODECLIENT CODEAXESTAT CODEELEMENTSTAT VALEURAXESTATISTIQUECLIENT
-------------------- ----------- --------------- ----------------------------------------
1000 5 1 JOHN
1001 5 1 ALICE
1002 5 1 JIM
1003 5 1 BOB
1000 9 1 MAN
1001 9 1 WOMAN
1002 9 1 unknown
1003 9 1 MAN
8 rows selected.
Query:
SQL> insert into stats_client_test
2 (codeclient, codeaxestat, codeelementstat, VALEURAXESTATISTIQUECLIENT)
3 select x.codeclient, 9, 1, 'unknown'
4 from (select codeclient from stats_client_Test
5 where codeaxestat = 5
6 minus
7 select codeclient from stats_client_Test
8 where codeaxestat = 9
9 ) x;
0 rows created.
After:
SQL> select * From stats_client_Test order by codeaxestat, codeclient;
CODECLIENT CODEAXESTAT CODEELEMENTSTAT VALEURAXESTATISTIQUECLIENT
-------------------- ----------- --------------- ----------------------------------------
1000 5 1 JOHN
1001 5 1 ALICE
1002 5 1 JIM
1003 5 1 BOB
1000 9 1 MAN
1001 9 1 WOMAN
1002 9 1 unknown --> here it is
1003 9 1 MAN
8 rows selected.
SQL>

How to flip values of two rows?

ID TYPE ORDER_INDEX CITY_ID
-----------------------------------
1 CAT 1 1
2 DOG 2 1
3 CAT 4 2
4 DOG 5 2
5 BEE 9 1
For each city I need to swap the order_index of cat and dog.
So city with ID 1 should have cat=2 and dog=1, city with ID 2 would have cat=5 and dog=4.
How can this be done with pure SQL?
Use an update:
UPDATE yourTable t1
SET ORDER_INDEX = (SELECT t2.ORDER_INDEX FROM yourTable t2
WHERE t2.CITY_ID = t1.CITY_ID AND
t2.TYPE IN ('CAT', 'DOG') AND
t2.TYPE <> T1.TYPE)
WHERE TYPE IN ('CAT', 'DOG');
The above update logic assumes that you wish to do the ORDER_INDEX swap between dog/cat pair records having the same CITY_ID.
Well, if you don't care about ID, then there's a simple option which does what you wanted:
Before:
SQL> select * from test order by id;
ID TYP ORDER_INDEX CITY_ID
---------- --- ----------- ----------
1 CAT 1 1
2 DOG 2 1
3 CAT 4 2
4 DOG 5 2
5 BEE 9 1
Update:
SQL> update test set
2 type = decode(type, 'DOG', 'CAT', 'DOG')
3 where type in ('CAT', 'DOG');
4 rows updated.
After; requirement was
city with ID 1 should have cat=2 and dog=1, city with ID 2 would have cat=5 and dog=4.
SQL> select * from test order by id;
ID TYP ORDER_INDEX CITY_ID
---------- --- ----------- ----------
1 DOG 1 1 --> city with ID = 1 has DOG = 1 ...
2 CAT 2 1 --> ... and CAT = 2
3 DOG 4 2 --> city with ID = 2 has DOG = 4 ...
4 CAT 5 2 --> ... and CAT = 5
5 BEE 9 1
SQL>

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

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)

Oracle Procedure to join two tables with latest status

Please help me make an oracle stored procedure ; I have two tables
tblLead:
lead_id Name
1 x
2 y
3 z
tblTransaction:
Tran_id lead_id date status
1 1 04/20/2010 call Later
2 1 05/05/2010 confirmed
I want a result like
lead_id Name status
1 x confirmed
2 y not available !
3 z not available !
Use an outer join to the relevant rows of tblTransaction:
SQL> SELECT l.lead_id, l.NAME,
2 CASE
3 WHEN t.status IS NULL THEN
4 'N/A'
5 ELSE
6 t.status
7 END status
8 FROM tbllead l
9 LEFT JOIN (SELECT lead_id,
10 MAX(status) KEEP(DENSE_RANK FIRST
11 ORDER BY adate DESC) status
12 FROM tbltransaction
13 GROUP BY lead_id) t ON l.lead_id = t.lead_id;
LEAD_ID NAME STATUS
---------- ---- ----------
1 x confirmed
2 y N/A
3 z N/A
Alternatively you can use analytics:
SQL> SELECT lead_id, NAME, status
2 FROM (SELECT l.lead_id, l.NAME,
3 CASE
4 WHEN t.status IS NULL THEN
5 'N/A'
6 ELSE
7 t.status
8 END status,
9 row_number()
10 over(PARTITION BY l.lead_id ORDER BY t.adate DESC) rn
11 FROM tbllead l
12 LEFT JOIN tbltransaction t ON l.lead_id = t.lead_id)
13 WHERE rn = 1;
LEAD_ID NAME STATUS
---------- ---- ----------
1 x confirmed
2 y N/A
3 z N/A
It can be written in plain SQL as follows,
SELECT lead_id, name, NVL(status,'not available !')
FROM (
SELECT tblLead.lead_id, tblLead.name, tblTransaction.status,
rank ( ) OVER (PARTITION BY tblTransaction.lead_id ORDER BY tblTransaction.datee DESC, tblTransaction.tran_id DESC) rank
FROM tblLead
LEFT JOIN tblTransaction ON tblLead.lead_id = tblTransaction.lead_id
)
WHERE rank = 1
ORDER BY lead_id;
Or you may think of writing a view as follows,
CREATE VIEW trx_view AS
------
------;
Personally I think stored procedure is not necessary for scenarios like this.

Resources