update value in table oracle depand on oldest date - sql

I have two tables (T1,T2) connected to each other by foreign key columns (B,C).
T1
C B A
11 1 123
12 2 123
13 3 123
14 4 222
15 5 222
16 6 333
T2
A2 B2 C2 D
1 11 25/4/1972
2 12 2/11/1982
3 13 4/6/2000
4 14 2/7/1992
5 15 14/2/2010
6 16 6/3/1999
I need to update A2 value(T2) from A value(T1) according to oldest date in column D (T2) which gives the following result:
T2
A2 B2 C2 D
123 1 11 25/4/1972
2 12 2/11/1982
3 13 4/6/2000
222 4 14 2/7/1992
5 15 14/2/2010
333 6 16 6/3/1999
show resulet

This is far from a smart & nice solution, but - might be OK until someone posts something better.
Test case for the rest of you (saving you some time, as Omar chose not to):
create table t1 (c number, b number, a number);
create table t2 (a2 number, b2 number, c2 number, d date);
insert into t1
select 11, 1, 123 from dual union
select 12, 2, 123 from dual union
select 13, 3, 123 from dual union
select 14, 4, 222 from dual union
select 15, 5, 222 from dual union
select 16, 6, 333 from dual;
insert into t2 (b2, c2, d)
select 1, 11, date '1972-04-25' from dual union
select 2, 12, date '1982-11-02' from dual union
select 3, 13, date '2000-06-04' from dual union
select 4, 14, date '1992-07-02' from dual union
select 5, 15, date '2010-02-14' from dual union
select 6, 16, date '1999-03-06' from dual;
First update every row, then remove unnecessary values:
SQL> update t2 set
2 t2.a2 = (select t1.a
3 from t1
4 where t1.b = t2.b2
5 and t1.c = t2.c2
6 );
6 rows updated.
SQL> update t2 set
2 t2.a2 = (select distinct
3 case when min(x.d) over (partition by x.a2) = t2.d then t2.a2
4 else null
5 end
6 from t2 x
7 where x.a2 = t2.a2
8 );
6 rows updated.
SQL> select * from t2;
A2 B2 C2 D
---------- ---------- ---------- ----------
123 1 11 25.04.1972
2 12 02.11.1982
3 13 04.06.2000
222 4 14 02.07.1992
5 15 14.02.2010
333 6 16 06.03.1999
6 rows selected.
SQL>

Assuming that the dates are unique (as in your example), you can do:
update t2
set t2.a2 = (select t1.a from t1 where t1.b = t2.b2)
where t2.d = (select min(tt2.d)
from t1 join
t2 tt2
on tt2.b2 = t1.b
group by t1.a
);
If you do have duplicates, you can change the where to:
where (t2.b2, t2.d) = (select min(tt2.b2) keep (dense_rank first over order by tt2.d), min(tt2.d)
from t1 join
t2 tt2
on tt2.b2 = t1.b
group by t1.a
)

Related

How can I select a data from another column from rows that have been selected?

I tried my best to figure and google this out, but couldn't really find a solid answer to it.
The problem I'm facing is that
Table 1:
ID Value 1
1 a
2 b
3 c
Table 2:
ID Value 2
1 4a
3 5b
4 6c
and I'd basically have to select the value from Table 1 that doesn't exist on Table 2 (Thus, 'b')
I can select and identify the ID that I want by using minus function between the tables, but can't seem to figure out a way to call a query to instead call the data.
Use the MINUS as a subquery (i.e. an inline view) (lines #14 - 16):
Sample data:
SQL> with
2 table1(id, value1) as
3 (select 1, 'a' from dual union all
4 select 2, 'b' from dual union all
5 select 3, 'c' from dual
6 ),
7 table2 (id, value2) as
8 (select 1, '4a' from dual union all
9 select 3, '5b' from dual union all
10 select 4, '6c' from dual
11 )
Query begins here:
12 select a.*
13 from table1 a
14 where a.id in (select t1.id from table1 t1
15 minus
16 select t2.id from table2 t2
17 );
ID VALUE1
---------- ----------
2 b
SQL>
Alternatively, use not exists:
<snip>
12 select a.*
13 from table1 a
14 where not exists (select null
15 from table2 b
16 where b.id = a.id
17 );
ID VALUE1
---------- ----------
2 b
SQL>

oracle moving where clause from join to outer join - not returning records properly

I have a query which as follows:
select c.* from cases c
left outer join (select case_id, case_status_id from case_status where case_id not in (SELECT case_id
FROM case_status
where (case_status_id = 16 and case_status_date < sysdate - 365))) cs ON cs.case_id = c.case_id
left outer join lkp_case_status lkp_cs
on lkp_cs.id = cs.case_status_id
where c.case_type = 'P'
and c.delete_date is null
The 2nd line used to be a join earlier but now I had to convert it to left outer join. What it does is it checks for case status and if the case status is 16 and the date is a year back don't show the record. When I change it to left outer join it picks up the record even when the dates are in 2019.
case
id name
1 AAA
2 BBB
3 CCC
4 DDD
case_status
1 16 01-NOV-19 03.42.37.420000000 PM
1 5 01-NOV-19 03.42.37.420000000 PM
2 1 18-NOV-19 12.36.11.268000000 PM
2 3 18-NOV-19 12.36.11.268000000 PM
3 5 18-NOV-21 12.36.11.268000000 PM
3 16 18-NOV-21 12.36.11.268000000 PM
The output should show as follows:
Result:
id name status
2 BBB 1,3
3 CCC 5,16
4 DDD
case id 1 has status 16 and dates back to 2019, case id 3 though has status 16 is within a year so should be picked up.
The query is really big but I am missing only this case status scenario hence posting a part of it.
Any suggestions or inputs, please.
How about not exists?
Sample data:
SQL> with
2 tcase (id, name) as
3 (select 1, 'AAA' from dual union all
4 select 2, 'BBB' from dual union all
5 select 3, 'CCC' from dual union all
6 select 4, 'DDD' from dual
7 ),
8 tcase_status (case_id, case_status_id, case_status_date) as
9 (select 1, 16, date '2019-11-01' from dual union all
10 select 1, 5, date '2019-11-01' from dual union all
11 select 2, 1, date '2019-11-18' from dual union all
12 select 2, 3, date '2019-11-18' from dual union all
13 select 3, 5, date '2021-11-18' from dual union all
14 select 3, 16, date '2021-11-18' from dual
15 )
Query:
16 select c.id,
17 c.name,
18 listagg(cs.case_status_id, ', ') within group (order by cs.case_status_id) status
19 from tcase c left join tcase_status cs on c.id = cs.case_id
20 where not exists (select null
21 from tcase_status c
22 where c.case_id = cs.case_id
23 and c.case_status_id = 16
24 and c.case_status_date < add_months(trunc(sysdate), -12)
25 )
26 group by c.id, c.name
27 order by c.id;
ID NAME STATUS
---------- ---- ----------
2 BBB 1, 3
3 CCC 5, 16
4 DDD
SQL>

Oracle - How use string data in (in operator)

In tbl_1 I have:
id
1
2
3
4
5
6
7
8
9
in tbl_2:
id value
1 1,2,3
2 5
Select * from tbl_1 where id in (Select value from tbl_2 where id = 2); --is OK
Select * from tbl_1 where id in (Select value from tbl_2 where id = 1);
--Need this resault: 3 rows: 1, 2 and 3
Fix your data model! You should not be storing numbers as strings. You should have properly declared foreign key relationships. Strings should not be used to store multiple values.
Sometimes, we are stuck with other people's really, really, really bad decisions. You can do what you want with `like:
Select t1.*
from tbl_1 t1
where exists (select 1
from tbl_2 t2
where t2.id = 1 and
',' || t2.value || ',' like '%,' || t1.id ',%'
);
However, your effort should be going into fixing the data, rather than trying to deal with it. The correct data would be a junction/association table with one row per id and value for table 2:
id value
1 1
1 2
1 3
2 5
Yet another option:
SQL> with
2 -- sample data
3 tbl_1 (id) as
4 (select 1 from dual union all
5 select 2 from dual union all
6 select 3 from dual union all
7 select 4 from dual union all
8 select 5 from dual union all
9 select 6 from dual union all
10 select 7 from dual union all
11 select 8 from dual union all
12 select 9 from dual
13 ),
14 tbl_2 (id, value) as
15 (select 1, '1,2,3' from dual union all
16 select 2, '5,6,7' from dual
17 )
18 -- query which returns what you want
19 select a.id
20 from tbl_1 a join
21 (select regexp_substr(b.value, '[^,]+', 1, column_value) id
22 from tbl_2 b cross join
23 table(cast(multiset(select level from dual
24 connect by level <= regexp_count(b.value, ',') + 1
25 ) as sys.odcinumberlist))
26 where b.id = 1
27 ) c on c.id = a.id;
ID
----------
1
2
3
SQL>
One option uses string functions:
select t1.*
from t1
inner join t2 on ',' || t2.value || ',' like '%,' || t1.id || ',%'
where t2.id = 1

How do I need to change my sql to get what I want in this case?

I have a table like following:
id value date
1 5 2015-01-10
2 5 2015-06-13
3 5 2015-09-05
4 11 2015-02-11
5 11 2015-01-10
6 11 2015-01-25
As can be seen, every value appears 3 times with different date. I want to write a query that returns the unique values that has the maximum date, which would be the following for the above table:
id value date
3 5 2015-09-05
4 11 2015-02-11
How could I do it?
This is the updated question:
The real question I am encountering is a little bit more complicated than the simplified version above. I thought I can move a step further once I know the answer to the simplified version, but I guest I was wrong. So, I am updating the question herein.
I have 2 tables like following:
Table 1
id id2 date
1 2 2015-01-10
2 5 2015-06-13
3 9 2015-09-05
4 10 2015-02-11
5 26 2015-01-10
6 65 2015-01-25
Table 2
id id2 data
1 2 A
2 5 A
3 9 A
4 10 B
5 26 B
6 65 B
Here, Table 1 and Table 2 are joined by id2
What I want to get is two records as follows:
id2 date data
9 2015-01-10 A
10 2015-02-11 B
You can use row_number to select the rows with the greatest date per value
select * from (
select t2.id2, t1.date, t2.data,
row_number() over (partition by t2.data order by t1.date desc) rn
from table1 t1
join table2 t2 on t1.id = t2.id2
) t where rn = 1
select a.id, a.value, a.date
from mytable a,
( select id, max(date) maxdate
from mytable b
group by id) b
where a.id = b.id
and a.date = b.maxdate;
Oracle Setup:
CREATE TABLE Table1 ( id, id2, "date" ) AS
SELECT 1, 2, DATE '2015-01-10' FROM DUAL UNION ALL
SELECT 2, 5, DATE '2015-06-13' FROM DUAL UNION ALL
SELECT 3, 9, DATE '2015-09-05' FROM DUAL UNION ALL
SELECT 4, 10, DATE '2015-02-11' FROM DUAL UNION ALL
SELECT 5, 26, DATE '2015-01-10' FROM DUAL UNION ALL
SELECT 6, 65, DATE '2015-01-25' FROM DUAL;
CREATE TABLE Table2 ( id, id2, data ) AS
SELECT 1, 2, 'A' FROM DUAL UNION ALL
SELECT 2, 5, 'A' FROM DUAL UNION ALL
SELECT 3, 9, 'A' FROM DUAL UNION ALL
SELECT 4, 10, 'B' FROM DUAL UNION ALL
SELECT 5, 26, 'B' FROM DUAL UNION ALL
SELECT 6, 65, 'B' FROM DUAL;
Query:
SELECT MAX( t1.id ) KEEP ( DENSE_RANK LAST ORDER BY t1."date" ) AS id,
MAX( t1.id2 ) KEEP ( DENSE_RANK LAST ORDER BY t1."date" ) AS id2,
MAX( t1."date" ) AS "date",
t2.data
FROM Table1 t1
INNER JOIN
Table2 t2
ON ( t1.id = t2.id AND t1.id2 = t2.id2 )
GROUP BY t2.data
Output:
ID ID2 date DATA
---------- ---------- ------------------- ----
3 9 2015-09-05 00:00:00 A
4 10 2015-02-11 00:00:00 B
Query 2:
SELECT id,
id2,
"date",
data
FROM (
SELECT t1.*,
t2.data,
ROW_NUMBER() OVER ( PARTITION BY t2.data ORDER BY t1."date" DESC ) AS rn
FROM Table1 t1
INNER JOIN
Table2 t2
ON ( t1.id = t2.id AND t1.id2 = t2.id2 )
)
WHERE rn = 1;
Output:
ID ID2 date DATA
---------- ---------- ------------------- ----
3 9 2015-09-05 00:00:00 A
4 10 2015-02-11 00:00:00 B

Oracle query to select rows with unique code

I have a table like this
C1 C2 C3 Code
1 2 3 33
1 2 3 34
2 4 1 14
1 2 3 14
i want to select only those record whose code is appearing only in single row. ie, in this case rows with code 33 and 34.. as they appear only once in this table.
How can i write a query for that
If you want only one pass over your data, then you can use this query:
SQL> create table mytable (c1,c2,c3,code)
2 as
3 select 1, 2, 3, 33 from dual union all
4 select 1, 2, 3, 34 from dual union all
5 select 2, 4, 1, 14 from dual union all
6 select 1, 2, 3, 14 from dual
7 /
Table created.
SQL> set autotrace on
SQL> select max(c1) c1
2 , max(c2) c2
3 , max(c3) c3
4 , code
5 from mytable
6 group by code
7 having count(*) = 1
8 /
C1 C2 C3 CODE
---------- ---------- ---------- ----------
1 2 3 33
1 2 3 34
2 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 FILTER
2 1 SORT (GROUP BY)
3 2 TABLE ACCESS (FULL) OF 'MYTABLE'
Regards,
Rob.
SELECT C1, C2, C3, Code FROM tablename
WHERE Code IN
(
SELECT Code FROM tablename
GROUP BY Code
HAVING count(Code) = 1
)
select C1, C2, C3, Code
from tablename T1
where not exists ( select T2.exclude
from tablename T2
where T2.Code = T1.Code
and T2.rowid <> T1.rowid
)
PS. Watch out for NULL values in the Code column