How can we do update,delete,insert in single merge - oracle

I have a source and target table. I was doing a simple merge where I was able to do insert and update operation simultaneously. I just added columns like insert date and update date to know when the record is inserted and updated. Then, I also added a flag columns which tells us that if the record is ever updated if yes then 'Y' else 'N'. I was successful till this but when I tried to use delete it doesn't allow me.I have seen many solutions in sql server but mine is oracle. I was doing a soft delete in which the record is not deleted from the target but just the flag column is updated to 'Y' if some record is deleted from the source table. But I was not able to do that. I used case also but of no use.To be clear I just want to update flag when there is delete in source. Can anybody tell me how can I do that with single merge statement? I have simple table student having attribute like name,address mobile no , state. I have made my query like this-
merge into target t using (SELECT CASE WHEN S.STUDENT_ID IS NULL THEN T.STUDENT_ID ELSE S.STUDENT_ID END
AS STUDENT_ID,S.NAME,S.ADRESS,S.STATE,S.MOBILE FROM STUDENTS S full JOIN TARGET T ON S.STUDENT_ID=T.STUDENT_ID) s
on (s.student_id = t.student_id)
when not matched then insert (student_id,name,address,state,mobile,insert_date,update_date)
values(s.student_id,s.name,s.adress,s.state,s.mobile,sysdate,sysdate)
when matched then update set t.name=s.name,t.address=s.adress, t.state=s.state,t.mobile=s.mobile,update_date=sysdate
where s.name!=t.name or s.adress!=t.address or s.state!=t.state or s.mobile!=t.mobile;
this time I used case with every attribute and I am able to solve it but every time I run it it shows the no of extra rows present in target merged like - 4 rows are merged I dont want that can anyone help my query is-
MERGE INTO TARGET T USING (SELECT CASE WHEN S.STUDENT_ID IS NULL THEN T.STUDENT_ID ELSE S.STUDENT_ID END
AS STUDENT_ID,S.NAME,S.ADRESS,S.STATE,S.MOBILE FROM STUDENTS S FULL JOIN TARGET T ON S.STUDENT_ID=T.STUDENT_ID) S
ON (S.STUDENT_ID=T.STUDENT_ID)
WHEN NOT MATCHED THEN INSERT
(STUDENT_ID,NAME,ADDRESS,STATE,MOBILE,INSERT_DATE,UPDATE_DATE,FLAG )
VALUES(S.STUDENT_ID,S.NAME,S.ADRESS,S.STATE,S.MOBILE,SYSDATE,SYSDATE, 'N')
WHEN MATCHED THEN UPDATE SET T.FLAG='Y' , t.name = case when s.name is null then T.NAME else S.name end ,
T.ADDRESS=case when s.ADRESS is null then T.ADDRESS else S.ADRESS end ,
t.STATE=case when s.STATE is null then T.STATE else S.STATE end ,
T.MOBILE=CASE WHEN S.MOBILE IS NULL THEN T.MOBILE ELSE S.MOBILE END
WHERE S.NAME!=T.NAME OR S.ADRESS!=T.ADDRESS OR S.STATE!=T.STATE OR S.MOBILE!=T.MOBILE or t.STUDENT_ID in
(SELECT TARGET.STUDENT_ID FROM
STUDENTS FULL JOIN TARGET ON STUDENTS.STUDENT_ID=TARGET.STUDENT_ID WHERE STUDENTS.STUDENT_ID IS NULL);

Answer to my question-
MERGE INTO TARGET T USING
(SELECT CASE
WHEN S.STUDENT_ID IS NULL THEN T.STUDENT_ID
ELSE S.STUDENT_ID
END AS STUDENT_ID,
S.NAME,
S.ADRESS,
S.STATE,
S.MOBILE
FROM STUDENTS S
FULL JOIN TARGET T ON S.STUDENT_ID=T.STUDENT_ID) S ON (S.STUDENT_ID=T.STUDENT_ID) WHEN NOT MATCHED THEN
INSERT (STUDENT_ID,
NAME,
ADDRESS,
STATE,
MOBILE,
INSERT_DATE,
UPDATE_DATE,
FLAG)
VALUES(S.STUDENT_ID,
S.NAME,
S.ADRESS,
S.STATE,
S.MOBILE,
SYSDATE,
SYSDATE,
'N') WHEN MATCHED THEN
UPDATE
SET T.FLAG= CASE
WHEN ( T.STUDENT_ID IN (SELECT TARGET.STUDENT_ID
FROM STUDENTS
FULL JOIN TARGET ON STUDENTS.STUDENT_ID=TARGET.STUDENT_ID
WHERE STUDENTS.STUDENT_ID IS NULL)) THEN 'Y'
ELSE 'N'
END,
T.NAME = CASE
WHEN S.NAME IS NULL THEN T.NAME
ELSE S.NAME
END,
T.ADDRESS=CASE
WHEN S.ADRESS IS NULL THEN T.ADDRESS
ELSE S.ADRESS
END,
T.STATE=CASE
WHEN S.STATE IS NULL THEN T.STATE
ELSE S.STATE
END,
T.MOBILE=CASE
WHEN S.MOBILE IS NULL THEN T.MOBILE
ELSE S.MOBILE
END,
T.UPDATE_DATE=SYSDATE ,
T.INSERT_DATE=SYSDATE
WHERE S.NAME!=T.NAME
OR S.ADRESS!=T.ADDRESS
OR S.STATE!=T.STATE
OR S.MOBILE!=T.MOBILE
OR T.STUDENT_ID IN
(SELECT TARGET.STUDENT_ID
FROM STUDENTS
FULL JOIN TARGET ON STUDENTS.STUDENT_ID=TARGET.STUDENT_ID
WHERE STUDENTS.STUDENT_ID IS NULL)
AND FLAG!='Y';

Related

Data exists within database but doesnt show up on webi report

Hi I am building a v simple report as below; when I include the customer reference number and filter for another column (resolved time) being NULL then the number of incidents are reduced. (I know they exist within the database with the same filters)
(only INC1988464 is showing when more incidents should be showing)
Is there a way to test issues such as this in webi? Or a way to resolve this? Thanks in advance
Here is the sql used to make the report:
SELECT
'INC'||TRIM(to_char(ead_incident.incident,'0000000')),
ead_incident_credit.circuit_ref,
ead_incident_credit.customer_ref,
ead_incident_credit.data_rate,
ead_incident_credit.connection_type,
ead_incident_credit.completed_date,
ead_incident_credit.wholesaler_name,
ead_incident_credit.opened_datetime,
ead_incident_credit.resolved_datetime,
ead_incident_credit.resolution_details,
ead_incident_credit.resolution_duration_seconds,
ead_incident_credit.access_circuit_core_network,
ead_incident_credit.charge_amount,
ead_incident_credit.or_cost,
ead_incident_credit.sky_cost,
ead_incident_credit.service_credit_applicable,
ead_incident_credit.service_credit_due,
CASE WHEN COALESCE(COALESCE(ead_incident.actual_end_datetime,ead_incident.impact_end_datetime),ead_incident.resolved_datetime) IS NOT NULL THEN CASE WHEN ead_incident.impact_type = 'Full Outage' AND COALESCE(ead_incident.cause_classification,'') <> 'Resolved - No fault found' AND COALESCE(odwh_data.ead_within_sla('TTR',ead_incident.opened_datetime, COALESCE(COALESCE(ead_incident.actual_end_datetime,ead_incident.impact_end_datetime),ead_incident.resolved_datetime)),'f') = 'f' THEN 'Y' ELSE 'N' END ELSE NULL END,
CASE WHEN ead_incident.correlation = 't' THEN 'Y' ELSE 'N' END,
odwh_system.kpi_nextweek_startdate(),
odwh_system.kpi_currentweek_startdate()
FROM
odwh_data.ead_incident ead_incident INNER JOIN odwh_data.ead_incident_credit ead_incident_credit ON (ead_incident_credit.incident=ead_incident.incident)
WHERE
(
CASE WHEN COALESCE(COALESCE(ead_incident.actual_end_datetime,ead_incident.impact_end_datetime),ead_incident.resolved_datetime) IS NOT NULL THEN CASE WHEN ead_incident.impact_type = 'Full Outage' AND COALESCE(ead_incident.cause_classification,'') <> 'Resolved - No fault found' AND COALESCE(odwh_data.ead_within_sla('TTR',ead_incident.opened_datetime, COALESCE(COALESCE(ead_incident.actual_end_datetime,ead_incident.impact_end_datetime),ead_incident.resolved_datetime)),'f') = 'f' THEN 'Y' ELSE 'N' END ELSE NULL END = 'Y'
AND
(
CASE WHEN ead_incident.deleted = 't' THEN 'Y' ELSE 'N' END = 'N'
AND
(
CASE WHEN ead_incident.wholesaler = 't' THEN 'Y' WHEN ead_incident.wholesaler = 'f' THEN 'N' END = 'Y'
OR
CASE WHEN ead_incident.wholesaler = 't' THEN 'Y' WHEN ead_incident.wholesaler = 'f' THEN 'N' END Is Null
)
)
)
If you can create a free-hand SQL query which exhibits your issue like this...
SELECT
'INC1988464' AS [Incident Number]
, 'SKY-COLT-003' AS [Customer Reference]
, CONVERT (DATETIME, '2022-05-08 11:17:49') AS [Resovled Time]
UNION
SELECT
'INC1988464' AS [Incident Number]
, 'SKY-COLT-005' AS [Custome rReference]
, CONVERT (DATETIME, '2022-05-09 9:03:21') AS [Resovled Time]
UNION
SELECT
'INC1988464' AS [Incident Number]
, 'SKY-COLT-007' AS [Customer Reference]
, NULL AS [Resovled Time]
UNION
SELECT
'INC1988464' AS [Incident Number]
, 'SKY-COLT-009' AS [Customer Reference]
, CONVERT (DATETIME, '2022-05-09 10:17:02') AS [Resovled Time];
Then we can take that query, put it into a WebI report, replicate the issue, and possibly resolve it.
First and easiest thing to check. Do you have any filters already on your report page or are you starting on a brand new page? The filters may be against the page or the individual report block.
If you have, then remove them and see if the issue is resolved. Note that there may also be a ranking in there, returning the top 1 incidents.
If not, then there will be something in the report logic preventing the records from being returned. Comment out the where clauses and make sure to add the columns from the where clauses into your result set. From here you have two things to check:
1/ All rows are being returned
2/ You can see which rows are going to be filtered by your where clause based on what you see on your report based on your logic. You have nested case expressions which may not be doing what you expected for example.

Joining 3 tables doesnt work, should I use a subquery for the below

I have this query.
SELECT re_current_benefits.prospect_nbr, qo_quote.quote_id, hra_ind, quote_type, effective_date, quote_expiration_ind, mhs_lob_code
FROM re_current_benefits
INNER JOIN qo_quote ON qo_quote.prospect_nbr = re_current_benefits.prospect_nbr
WHERE hra_ind = 'Y' AND quote_type = 'R' AND quote_expiration_ind IS NULL AND mhs_lob_code NOT IN 'DTL1' AND mhs_lob_code NOT IN 'DTL2'
And I need to pull the prospect_nbr from here, I also need to add a filter saying effective_date = renewal_date but if I do that by joining all 3 tables together I get 0 results which should not be the case.
SELECT prospect_nbr, rmr_run_status, renewal_date
FROM re_group
WHERE rmr_run_status = ANY ('S', 'W')

cant update a column from table it returns single-row subquery returns more than one

When i execute the following query, i get the message like
Ora-01427 single-row subquery returns more than one row
I am trying to update "City" column in Table A From another table.
How would I do this?
table A: Name, PER_code(it also has duplicated value or null), city, PD_code
table B: Name, PER_code(No duplicated value,Maybe null), city, Postal_code
The update statement:
UPDATE A
SET (A.city) =
(SELECT B.city
FROM B
INNER JOIN A
ON A.per_code=B.per_code
WHERE A.per_code is not null)
Since there are duplicate values but you select only one field, you modify your query from
UPDATE A SET (A.city) = (SELECT B.city FROM B INNER JOIN A ON
A.per_code=B.per_code WHERE A.per_code is not null)
to
UPDATE A SET (A.city) = (SELECT DISTINCT B.city FROM B INNER JOIN A ON
A.per_code=B.per_code WHERE A.per_code is not null)
The distinct operator will allow you to keep a single value if it's duplicated in the table B. If there are multiple distinct values, you will have to look at your data and make a decision about which value should be used in the other table.
You can also try MERGE INTO selecting DISTINCT records.
MERGE INTO A d
USING
( SELECT DISTINCT per_code, city FROM B ) s
ON ( s.per_code = d.per_code )
WHEN MATCHED THEN UPDATE SET d.City = s.City
WHERE d.per_code IS NOT NULL;
I think you intend a correlated subquery:
UPDATE A
SET city = (SELECT B.city
FROM B
WHERE A.per_code = B.per_code
)
WHERE A.per_code is not null;
EDIT:
The above should work given the constraints in the original question. If it does not, it is easily adaptable:
UPDATE A
SET city = (SELECT B.city
FROM B
WHERE A.per_code = B.per_code AND rownum = 1
)
WHERE A.per_code is not null;

Oracle procedure 'with query' insert into table

I have created an Oracle SQL query in TOAD which works fine. I now need to put this in a procedure.
The query has to create two counts based on different criteria (I have used With Select) and insert these plus a date and location to a table.
The query that works is
with
Selected_animals as
( SELECT TO_CHAR(SYSDATE,'DD/MM/YYYY' ) as Report_Date,
loc.name location,
count(rran.id) as Count_exported
FROM rr_animals rran,
contact con,
locations loc,
names nam
WHERE con.connum = rran.connum
AND con.loc_id = loc.id
AND con.connum = nam.connum
AND nam.name_type = 'STAND'
AND nam.dob IS NOT NULL
AND rran.sex IS NOT NULL
AND rran.web_display = 'Y'
AND rran.web_description IS NOT NULL
AND rran.visit_end_date IS NULL
AND con.loc_id IS NOT NULL
AND con.datedl IS NULL
AND rran.hold_user IS NULL
AND rran.assess_status IS NULL
AND EXISTS (SELECT rrim.id
FROM rr_images rrim
WHERE rrim.image_type = 'KENNEL'
AND rrim.rran_id = rran.id
AND DBMS_LOB.GETLENGTH(rrim.image_object) >0
AND rrim.image_object IS NOT NULL)
group by loc.NAME ),
total_animals as
(select vbav.sitename as location,
count(vbav.rran_ID) as Count_available
from v_bx_all_animal_visits vbav
where visit_end_date is null
and concat != 'DELTD'
and concat != 'DSCD'
group by vbav.sitename)
select Total_animals.location,
Selected_animals.Report_date,
Selected_animals.count_exported,
Total_animals.count_available
from Selected_animals, total_animals
where total_animals.location = selected_animals.location(+)
I have looked at several ways that seem to write the procedure but nothing seems to work. Including which was added under the CREATE or REPLACE and before BEGIN:
( o_location out bx_webstats_export_available.LOCATION%TYPE,
o_date out bx_webstats_export_available.REPORT_DATE%TYPE,
o_exported out bx_webstats_export_available.COUNT_EXPORTED%TYPE,
o_available out bx_webstats_export_available.COUNT_AVAILABLE%TYPE )
Also added after the last where statement and before End:
INSERT INTO bx_webstats_export_available(location, report_date, count_export, count_available)
values (Total_animals.location,
Selected_animals.Report_date,
Selected_animals.count_exported,
Total_animals.count_available);
Can anyone help me get this query in a Procedure please?
This is the first time I have written a Procedure from scratch and I'm struggling with it.
Many thanks,
What is it you're trying to do? Insert the results of that select into a table? If so, the following ought to suffice:
create or replace procedure your_proc_name
as
begin
insert into bx_webstats_export_available(location, report_date, count_export, count_available)
with selected_animals as (select to_char(sysdate,'DD/MM/YYYY' ) as report_date,
loc.name location,
count(rran.id) as count_exported
from rr_animals rran,
contact con,
locations loc,
names nam
where con.connum = rran.connum
and con.loc_id = loc.id
and con.connum = nam.connum
and nam.name_type = 'STAND'
and nam.dob is not null
and rran.sex is not null
and rran.web_display = 'Y'
and rran.web_description is not null
and rran.visit_end_date is null
and con.loc_id is not null
and con.datedl is null
and rran.hold_user is null
and rran.assess_status is null
and exists (select rrim.id
from rr_images rrim
where rrim.image_type = 'KENNEL'
and rrim.rran_id = rran.id
and dbms_lob.getlength(rrim.image_object) >0
and rrim.image_object is not null)
group by loc.name),
total_animals as (select vbav.sitename as location,
count(vbav.rran_id) as count_available
from v_bx_all_animal_visits vbav
where visit_end_date is null
and concat != 'DELTD'
and concat != 'DSCD'
group by vbav.sitename)
select total_animals.location,
selected_animals.report_date,
selected_animals.count_exported,
total_animals.count_available
from selected_animals, total_animals
where total_animals.location = selected_animals.location(+);
end your_proc_name;
/
If not, then please explain a bit more about the requirements you're trying to satisfy.

Reference parent query column in subquery (Oracle)

How can I reference a column outside of a subquery using Oracle? I specifically need to use it in the WHERE statement of the subquery.
Basically I have this:
SELECT Item.ItemNo, Item.Group
FROM Item
LEFT OUTER JOIN (SELECT Attribute.Group, COUNT(1) CT
FROM Attribute
WHERE Attribute.ItemNo=12345) A ON A.Group = Item.Group
WHERE Item.ItemNo=12345
I'd like to change WHERE Attribute.ItemNo=12345 to WHERE Attribute.ItemNo=Item.ItemNo in the subquery, but I can't figure out if this is possible. I keep getting "ORA-00904: 'Item'.'ItemNo': Invalid Identifier"
EDIT:
Ok, this is why I need this kind of structure:
I want to be able to get a count of the "Error" records (where the item is missing a value) and the "OK" records (where the item has a value).
The way I have set it up in the fiddle returns the correct data. I think I might just end up filling in the value in each of the subqueries, since this would probably be the easiest way. Sorry if my data structures are a little convoluted. I can explain if need be.
My tables are:
create table itemcountry(
itemno number,
country nchar(3),
imgroup varchar2(10),
imtariff varchar2(20),
exgroup varchar2(10),
extariff varchar2(20) );
create table itemattribute(
attributeid varchar2(10),
tariffgroup varchar2(10),
tariffno varchar2(10) );
create table icav(
itemno number,
attributeid varchar2(10),
value varchar2(10) );
and my query so far is:
select itemno, country, imgroup, imtariff, im.error "imerror", im.ok "imok", exgroup, extariff, ex.error "exerror", ex.ok "exok"
from itemcountry
left outer join (select sum(case when icav.itemno is null then 1 else 0 end) error, sum(case when icav.itemno is not null then 1 else 0 end) ok, tariffgroup, tariffno
from itemattribute ia
left outer join icav on ia.attributeid=icav.attributeid
where (icav.itemno=12345 or icav.itemno is null)
group by tariffgroup, tariffno) im on im.tariffgroup=imgroup and imtariff=im.tariffno
left outer join (select sum(case when icav.itemno is null then 1 else 0 end) error, sum(case when icav.itemno is not null then 1 else 0 end) ok, tariffgroup, tariffno
from itemattribute ia
left outer join icav on ia.attributeid=icav.attributeid
where (icav.itemno=12345 or icav.itemno is null)
group by tariffgroup, tariffno) ex on ex.tariffgroup=exgroup and extariff=ex.tariffno
where itemno=12345;
It's also set up in a SQL Fiddle.
You can do it in a sub-query but not in a join. In your case I don't see any need to. You can put it in the join condition.
select i.itemno, i.group
from item i
left outer join ( select group, itemno
from attribute b
group by group itemno ) a
on a.group = i.group
and i.itemno = a.itemno
where i.itemno = 12345
The optimizer is built to deal with this sort of situation so utilise it!
I've changed the count(1) to a group by as you need to group by all columns that aren't aggregated.
I'm assuming that your actual query is more complicated than this as with the columns you're selecting this is probably equivilent to
select itemno, group
from item
where itemno = 12345
You could also write your sub-query with an analytic function instead. Something like count(*) over ( partition by group).
As an aside using a keyword as a column name, in this case group is A Bad Idea TM. It can cause a lot of confusion. As you can see from the code above you have a lot of groups in there.
So, based on your SQL-Fiddle, which I've added to the question I think you're looking for something like the following, which doesn't look much better. I suspect, given time, I could make it simpler. On another side note explicitly lower casing queries is never worth the hassle it causes. I've followed your naming convention though.
with sub_query as (
select count(*) - count(icav.itemno) as error
, count(icav.itemno) as ok
, min(itemno) over () as itemno
, tariffgroup
, tariffno
from itemattribute ia
left outer join icav
on ia.attributeid = icav.attributeid
group by icav.itemno
, tariffgroup
, tariffno
)
select ic.itemno, ic.country, ic.imgroup, ic.imtariff
, sum(im.error) as "imerror", sum(im.ok) as "imok"
, ic.exgroup, ic.extariff
, sum(ex.error) as "exerror", sum(ex.ok) as "exok"
from itemcountry ic
left outer join sub_query im
on ic.imgroup = im.tariffgroup
and ic.imtariff = im.tariffno
and ic.itemno = im.itemno
left outer join sub_query ex
on ic.exgroup = ex.tariffgroup
and ic.extariff = ex.tariffno
and ic.itemno = ex.itemno
where ic.itemno = 12345
group by ic.itemno, ic.country
, ic.imgroup, ic.imtariff
, ic.exgroup, ic.extariff
;
You can put WHERE attribute.itemno=item.itemno inside the subquery. You are going to filter the data anyway, filtering the data inside the subquery is usually faster too.

Resources