Oracle update statement with analytics is failing - oracle

The Table_B contains duplicate records as seen below. What I am trying to do is lookup Table_A using Table_B.SERIAL_NUM and update Table_A .COVERAGE_END coloumn with the most recent Date value in COVERAGE_TO coloumn in Table_B.
Table_B
SERIAL_NUM ,COVERAGE_FROM ,COVERAGE_TO
123456 ,26/12/2014 ,13/12/2015
123456 ,14/12/2015 ,13/12/2016
23456 ,18/12/2014 ,13/12/2015
23456 ,14/12/2015 ,13/12/2016
Following is the update statement I am using.
update Table_A J
set ( J.COVERAGE_END_DATE)
=(select COVERAGE_TO from
(
select SERIAL_NUM, COVERAGE_TO, row_number() over(partition by SERIAL_NUM order by COVERAGE_TO desc) as rn from TABLE_B B
)where rn = 1
)
where exists
(
select * from TABLE_B Q where Q.SERIAL_NUM = J.SERIAL_NUMBER
)
;
However I get the following error
ORA-01427: single-row subquery returns more than one row
Can Some one please help me with this Query?

You have to compare J's SERIAL_NUMBER with B's to get the only proper value.
where rn = 1 and J.SERIAL_NUMBER = SERIAL_NUM
If not, subquery will return max values for every SERIAL_NUM.

I'm not sure why partitioning is needed here.
Would a simple MAX() not produce the needed result?
update table_a
set coverage_end_date = (
select max(coverage_to)
from table_b b inner join table_a a on b.serial_num = a.serial_num
)
;

Related

Oracle SQL -- Finding count of rows that match date maximum in table

I am trying to use a query to return the count from rows such that the date of the rows matches the maximum date for that column in the table.
Oracle SQL: version 11.2:
The following syntax would seem to be correct (to me), and it compiles and runs. However, instead of returning JUST the count for the maximum, it returns several counts more or less like the "HAIVNG" clause wasn't there.
Select ourDate, Count(1) as OUR_COUNT
from schema1.table1
group by ourDate
HAVING ourDate = max(ourDate) ;
How can this be fixed, please?
You can use:
SELECT MAX(ourDate) AS ourDate,
COUNT(*) KEEP (DENSE_RANK LAST ORDER BY ourDate) AS ourCount
FROM schema1.table1
or:
SELECT ourDate,
COUNT(*) AS our_count
FROM (
SELECT ourDate,
RANK() OVER (ORDER BY ourDate DESC) AS rnk
FROM schema1.table1
)
WHERE rnk = 1
GROUP BY ourDate
Which, for the sample data:
CREATE TABLE table1 (ourDate) AS
SELECT SYSDATE FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT SYSDATE - 1 FROM DUAL;
Both output:
OURDATE
OUR_COUNT
2022-06-28 13:35:01
5
db<>fiddle here
I don't know if I understand what you want. Try this:
Select x.ourDate, Count(1) as OUR_COUNT
from schema1.table1 x
where x.ourDate = (select max(y.ourDate) from schema1.table1 y)
group by x.ourDate
One option is to use a subquery which fetches maximum date:
select ourdate, count(*)
from table1
where ourdate = (select max(ourdate)
from table1)
group by ourdate;
Or, a more modern approach (if your database version supports it; 11g doesn't, though):
select ourdate, count(*)
from table1
group by ourdate
order by ourdate desc
fetch first 1 rows only;
You can use this SQL query:
select MAX(ourDate),COUNT(1) as OUR_COUNT
from schema1.table1
where ourDate = (select MAX(ourDate) from schema1.table1)
group by ourDate;

ORACLE Query to find value in other table based on dates

I have two tables, Table A has an ID and an Event Date and Table B has an ID, a Description and an Event Date.
Not all IDs in Table A appear in Table B and some IDs appear multiple times in Table B with different Descriptions for each event.
The Description in Table B is an attribute that can change over time, the Event date in Table B is the date that a given ID's Description changes from its default value (kept in another table) to the new value.
I want to find the Description in Table B that matches the Event Date in Table A so, for example
Table Sample Data
A1234 would return Green and A4567 would return Null
I can't create tables here so I need to be able to this with a query.
This query will select last description from before the event:
SELECT * FROM (
SELECT tabA.id, tabA.event_date, tabB.description,
ROW_NUMBER() OVER(PARTITION BY tabB.id ORDER BY tabB.event_date DESC) rn
FROM Table_A tabA
LEFT JOIN Table_B tabB ON tabA.id = tabB.id AND tabB.event_date <= tabA.event_date
) WHERE rn = 1
If I understand well your need, this could be a way:
select a.id, description
from tableA A
left join
(select id,
description,
event_date from_date,
lead(event_date) over (partition by id order by event_date) -1 as to_date
from tableB
) B
on (A.id = B.id and a.event_date between b.from_date and b.to_date)
The idea here is to evaluate, for each row in tableB the range of dates for which that row, and its description, is valid; given this, a simple join should do the job.
You can left join tables like:
select a.ID , b1.DESCRIPTION
from TABLE_A a
left join TABLE_B b1 on a.ID = b1.id and a.EVENT_DATE > b1.EVENT_DATE
left join TABLE_B b2 on a.ID = b2.id and b1.EVENT_DATE < b2.EVENT_DATE and a.EVENT_DATE > b2.EVENT_DATE
where b1.id is null or b2.EVENT_DATE is null;

Appropriate Join/Connection For This Scenario?

I want to join two tables together which I have done
I also want to join them based on a condition, where a particular column has a specific value, and I also have done this successfully. I used an inner join and a where clause so far.
However, for this result set, I want to further filter it by selecting ONLY the columns where a particular string appears more than once for a set of columns, eg;
employee_ID and CERTIFICATE
I'd like to group where employee_id has CERTIFICATE count > 2. This is after I have joined the tables together using a where clause.
I am perhaps thinking of using a subquery in my WHERE clause (which is the 3rd line that is also last)
For further clarification, I want to display only employees who have a certificate count greater than 2. By certificate, I am referencing a table with a string 'Certificate' under a column 'Skill'. In other words, select only columns where the string 'Certificate' appears TWICE for a particular employee ID.
To get just the employee ids:
SELECT t1.employee_id
FROM table1 t1
INNER JOIN
table2 t2
ON ( t1.col1 = t2.col1 )
GROUP BY t1.employee_id
HAVING COUNT( CASE t2.skill WHEN 'CERTIFICATE' THEN 1 END ) > 1
Or, to get all the columns:
SELECT *
FROM (
SELECT t1.*,
t2.*,
COUNT( CASE t2.skill WHEN 'CERTIFICATE' THEN 1 END )
OVER ( PARTITION BY t1.employee_id )
AS num_certificate
FROM table1 t1
INNER JOIN
table2 t2
ON ( t1.col1 = t2.col1 )
)
WHERE num_certificate > 1

Oracle join rows in order where order is defined differently on each table

I have two tables
TABLE_A with columns project_id, id and load_date
and TABLE_B with columns project_id, delete_flag and delete_date
where TABLE_A.load_date is a new column and I want to populate it based on TABLE_B.delete_date for historic data. Basically, a file has been repeatedly loaded into the system and historically we didn't keep track of when it was loaded. However, each time the file is re-loaded, the previous version of it is updated in TABLE_B with a delete_date (i.e. a soft delete). The previous version just stays in TABLE_A without any changes.
I would like to populate TABLE_A.load_date based on matching projects in TABLE_B. The oldest row in TABLE_A (smallest TABLE_A.id) matches the oldest row in TABLE_B (oldest delete_date), etc. So the rows should match up if you keep picking the next one in order from each table. But I don't know how to turn that into an Oracle statement. What I've got so far is this which doesn't deal with matching on row order:
MERGE INTO TABLE_A a
USING
(
SELECT PROJECT_ID, DELETE_DATE
FROM TABLE_B
WHERE DELETE_FLAG = 'Y'
ORDER BY DELETE_DATE ASC
) b ON (a.PROJECT_ID = b.PROJECT_ID)
WHEN MATCHED THEN UPDATE
SET a.LOAD_DATE = p.DELETE_DATE;
This merge should do the work, as far as I properly understood your criteria:
merge into table_a ta
using (
select pid project_id, id, delete_date
from (
select project_id pid, id,
row_number() over (partition by project_id order by id) rn
from table_a) a
join (
select project_id pid, delete_date,
row_number() over (partition by project_id order by delete_date ) rn
from table_b
where delete_flag='Y') b using (pid, rn) ) tb
on (ta.project_id = tb.project_id and ta.id = tb.id)
when matched then update
set ta.load_date = tb.delete_date

ORACLE Update with MINUS result

In Oracle 10g, I want to update the records of the resulting minus query below:
(
SELECT A,B,C FROM Table1
MINUS
SELECT A,B,C FROM Table2
)
The column that is to be updated is not part of the minus query as its not present in both tables so the below code is not an option
UPDATE
(
SELECT A,B,C FROM Table1
MINUS
SELECT A,B,C FROM Table2
)
SET TABLE1.D = 'TEST'
How about:
update table1
set d = 'TEST'
where (a,b,c) not in(select a,b,c from table2);
Edit:
The performance of minus generally suck, due to the sort operation.
If any of {a,b,c} are nullable, try the following instead:
update table1 t1
set t1.d = 'TEST'
where not exists(
select 'x'
from table2 t2
where t2.a = t1.a
and t2.b = t1.b
and t2.c = t1.c
);
In response to your comment about wanting to use the minus clause:
update Table1
set d = 'TEST'
where (a,b,c) in (select a,b,c from Table1 minus select a,b,c from Table2);

Resources