I have a table TABLE1
with 3 columns NAME, ROLLNO, CASHDATE
.
CREATE TABLE TABLE1
(
NAME VARCHAR2(4) NOT NULL,
ROLLNO NUMBER(4) NOT NULL,
CASHDATE VARCHAR2(8) NOT NULL
);
INSERT INTO TABLE1 VALUES('SAMY', 1234, '15990101');
INSERT INTO TABLE1 VALUES('TOMY', 1324, '15990101');
INSERT INTO TABLE1 VALUES('DANY', 1342, '15990101');
TABLE1 looks like:
NAME ROLLNO CASHDATE
----------------------------------
SAMY 1234 15990101
TOMY 1324 15990101
DANY 1342 15990101
CASHDATE value is in the form of YYYYMMDD
I have a second table TABLE2
with 3 columns NAME, ID, ID_DATE
:
CREATE TABLE TABLE2
(
NAME VARCHAR2(4) NOT NULL,
ID VARCHAR2(2) NOT NULL,
ID_DATE TIMESTAMP(3)
);
INSERT INTO TABLE2 VALUES('SAMY', '01', timestamp '2021-08-21 00:00:00');
INSERT INTO TABLE2 VALUES('SAMY', 'A1', timestamp '2018-08-19 00:00:00');
INSERT INTO TABLE2 VALUES('TOMY', '01', timestamp '2021-08-22 00:00:00');
INSERT INTO TABLE2 VALUES('TOMY', 'B1', timestamp '2000-08-15 00:00:00');
TABLE2 looks like:
NAME ID ID_DATE
--------------------------------------------------------
SAMY 01 21-AUG-2021 12.00.00.000000000 AM
SAMY A1 19-AUG-2018 12.00.00.000000000 AM
TOMY 01 22-AUG-2021 12.00.00.000000000 AM
TOMY B1 15-AUG-2000 12.00.00.000000000 AM
And I have a third table TABLE3
with 2 columns NAME, SEC_DATE
:
CREATE TABLE TABLE3
(
NAME VARCHAR2(4) NOT NULL,
SEC_DATE TIMESTAMP(3)
);
INSERT INTO TABLE3 VALUES('SAMY', timestamp '2021-08-21 00:00:00');
INSERT INTO TABLE3 VALUES('TOMY', timestamp '2021-08-22 00:00:00');
INSERT INTO TABLE3 VALUES('DANY', timestamp '2021-08-29 00:00:00');
TABLE3 looks like:
NAME SEC_DATE
----------------------------------------------
SAMY 21-AUG-2021 12.00.00.000000000 AM
TOMY 22-AUG-2021 12.00.00.000000000 AM
DANY 29-AUG-2021 12.00.00.000000000 AM
As we see I have a TABLE1 having CASHDATE value as 15990101 which is default value.
So we need to UPDATE CASHDATE column which is having 15990101 in TABLE1 based on following conditions.
- First it checks in TABLE2 based on NAME in TABLE2, if there is a record having same NAME with ID = '01' then ID_DATE value should update in CASHDATE column of TABLE1.
- If it is not found in TABLE2 based on NAME with ID = '01', then it checks in TABLE3 and based on NAME only if there is a record, we need to update (SEC_DATE - 1) value in CASHDATE column of TABLE1.
Finally after update, the result TABLE1 looks like:
NAME ROLLNO CASHDATE
----------------------------------
SAMY 1234 20210821 --This record found in TABLE2
TOMY 1324 20210822 --This record found in TABLE2
DANY 1342 20210828 --This record found in TABLE3 (SEC_DATE - 1)
I understand we need to update statement but I am not sure using CASE WHEN in UPDATE statement.
CodePudding user response:
You can use COALESCE
in combination with subqueries for this:
update table1 t1
set cashdate =
coalesce
(
(select to_char(id_date, 'yyyymmdd') from table2 t2 where t2.name = t1.name and t2.id = '01'),
(select to_char(sec_date - interval '1' day, 'yyyymmdd') from table3 t3 where t3.name = t1.name),
'15990101'
)
where cashdate = '15990101';
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=b6c5b08f5b303a23d26e5349228ee301
CodePudding user response:
From my point of view, a simple option is a two-step option, i.e. two updates. In this case, I actually prefer MERGE
over UPDATE
.
Initial table1
contents:
SQL> select * From table1;
NAME ROLLNO CASHDATE
---- ---------- --------
SAMY 1234 15990101
TOMY 1324 15990101
DANY 1342 15990101
Update cashdate
based on table2
's contents (that's your 1st condition):
SQL> merge into table1 a
2 using table2 b
3 on (a.name = b.name)
4 when matched then update set
5 a.cashdate = to_char(b.id_date, 'yyyymmdd')
6 where a.cashdate = '15990101'
7 and b.id = '01';
2 rows merged.
Update cashdate
based on table3
's contents (that's your 2st condition) (not exists
is here to skip rows already updated in previous step):
SQL> merge into table1 a
2 using table3 c
3 on (c.name = a.name)
4 when matched then update set
5 a.cashdate = to_char(c.sec_date - 1, 'yyyymmdd')
6 where a.cashdate = '15990101'
7 and not exists (select null from table2 b
8 where b.name = a.name
9 and b.id = '01'
10 );
1 row merged.
Final result:
SQL> select * from table1;
NAME ROLLNO CASHDATE
---- ---------- --------
SAMY 1234 20210821
TOMY 1324 20210822
DANY 1342 20210828
SQL>
CodePudding user response:
You can query the data and update the table rows from the query result. This is called an updateable query. The great advantage is that we only update rows that we want updated (in your case rows that are on default value and have a match in table2 and/or table3).
For a query to be updateable, Oracle must see it guaranteed that there is just one target value selected per row. This means that we need unique constraints to ensure that joining table2 and table3 rows to a table1 row still results in no more than one row per table1 row.
The constraints needed here are
- table2: primary key (name, id)
- table3: primary key (name)
With these constraints in place, Oracle should be able to update our query:
update
(
select
t1.cashdate,
coalesce(t2.id_date, t3.sec_date - interval '1' day) as found_date
from table1 t1
left join table2 t2 on t2.name = t1.name and t2.id = '01'
left join table3 t3 on t3.name = t1.name
where t1.cashdate = '15990101'
and coalesce(t2.id_date, t3.sec_date - interval '1' day) is not null
)
set cashdate = to_char(found_date, 'yyyymmdd');
This may still fail. It really depends on whether Oracle considers this query updateable. It should (and it does in Oracle 18c, as you can see in the demo inked below), but sometimes Oracle struggles with such queries. You'll have to try whether this already works in Oracle 11g.
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=36287406a7f5591d4b53df4a7b990ef6