Oracle customized merge - sql

We have two tables one is source and another one is target. The source table gets updated/refresh with external data source and we have a PL/SQL block which runs daily and updates/inserts the target table rows using Oracle's merge function.
Now we have a situation where a column (email) in the target table could get updated by some external source. Consequently, this email is being updated/overwritten by email of source table with the above mentioned PL/SQL's merge function.
Is there something in the merge function so that it only updates the rows in the target table if things other than the email have been changed. I am happy to add new column(s) into the source and target table if needed.
create or replace
PROCEDURE INSERT_UPDATE_TARGET AS
BEGIN
merge into INTEGRATION_TARGET t
using v_merge vm
on (t.person_num = vm.person_num)
when matched then update set T.YEAR_START = VM.YEAR_START,
T.SEMESTER_START = VM.SEMESTER_START,
T.SATAC_DATABASE_NAME = VM.SATAC_DATABASE_NAME,
T.STUDY_LEVEL = VM.STUDY_LEVEL,
T.REF_NUM = VM.REF_NUM,
T.SEX = VM.SEX,
T.DATE_OF_BIRTH = VM.DATE_OF_BIRTH,
T.DATE_RECEIVED = VM.DATE_RECEIVED,
T.TITLE = VM.TITLE,
T.FAMILY_NAME = VM.FAMILY_NAME,
T.GIVEN_NAME_1 = VM.GIVEN_NAME_1,
T.STREET_NAME = VM.STREET_NAME,
T.STREET_NAME_2 = VM.STREET_NAME_2,
T.STREET_NAME_3 = VM.STREET_NAME_3,
T.POSTAL_STATE = VM.POSTAL_STATE,
T.POSTAL_TOWN = VM.POSTAL_TOWN,
T.POSTAL_POSTCODE = VM.POSTAL_POSTCODE,
T.COUNTRY = VM.COUNTRY,
T.MOBILE = VM.MOBILE,
T.EMAIL = VM.EMAIL
when not matched then insert (T.YEAR_START,
T.SEMESTER_START,
T.SATAC_DATABASE_NAME,
T.STUDY_LEVEL,
T.REF_NUM,
T.PERSON_NUM,
T.SEX,
T.DATE_OF_BIRTH,
T.DATE_RECEIVED,
T.TITLE,
T.FAMILY_NAME,
T.GIVEN_NAME_1,
T.STREET_NAME,
T.STREET_NAME_2,
T.STREET_NAME_3,
T.POSTAL_STATE,
T.POSTAL_TOWN,
T.POSTAL_POSTCODE,
T.COUNTRY,
T.MOBILE,
T.EMAIL
)
values(VM.YEAR_START,
VM.SEMESTER_START,
VM.SATAC_DATABASE_NAME,
VM.STUDY_LEVEL,
VM.REF_NUM,
VM.PERSON_NUM,
VM.SEX,
VM.DATE_OF_BIRTH,
VM.DATE_RECEIVED,
VM.TITLE,
VM.FAMILY_NAME,
VM.GIVEN_NAME_1,
VM.STREET_NAME,
VM.STREET_NAME_2,
VM.STREET_NAME_3,
VM.POSTAL_STATE,
VM.POSTAL_TOWN,
VM.POSTAL_POSTCODE,
VM.COUNTRY,
VM.MOBILE,
VM.EMAIL
);
END;
Thanks

Add a where clause to your update statement

Related

Is it viable to build type 2 history in BiqQuery without knowing primary keys?

I am importing a lot of mainframe extracts into BigQuery daily. Each extract is a full export of all the data available. I've been loading the data into BigQuery and then generating a type 2 history using a SQL MERGE statement, where I join on the primary keys of each table and compare all columns to find differences, closing outdated rows and inserting new/updated rows into a history table. This works fine.
A colleague made the case that we don't need to know the primary keys to do this, we can just treat all data columns together as the unique constraint. With that in mind I created a new MERGE statement, which seems to work just as well as the old one, but without having to know the primary keys, which makes a lot of things easier for us. Here is an example of the new query:
MERGE ods.kfir_history AS main USING (
SELECT FF_NR, FIRMA_PRODENH_TYPE, FIRMA_NAVN_1, FIRMA_NAVN_2, SE_NR, KONCERN_NR, FIRMA_STATUS_DATO, FIRMA_STATUS_DATO_NUL, FIRMA_STATUS, FIRMA_TYPE, ANTAL_ANSAT_DATO, ANTAL_ANSAT_DATO_NUL, ANTAL_ANSAT, ANTAL_ANSAT_NUL, ANTAL_ANSAT_KILDE, FIRMA_STIFTET_DATO, FIRMA_STIFTET_DATO_NUL, AS_REGISTRERET, MOMS_REGISTRERET, FAGLIG_FORENING, HEKTAR, RET_SBH, RET_TIMESTAMP, SUPL_FIRMA_NAVN, SUPL_FIRMA_NAVN_NUL, CVR_NR, P_NR from staging.kfir_new
) AS delta ON
IFNULL(main.FF_NR,'null') = IFNULL(delta.FF_NR,'null') AND
IFNULL(main.FIRMA_PRODENH_TYPE,'null') = FNULL(delta.FIRMA_PRODENH_TYPE,'null') AND
IFNULL(main.FIRMA_NAVN_1,'null') = IFNULL(delta.FIRMA_NAVN_1,'null') AND
IFNULL(main.FIRMA_NAVN_2,'null') = IFNULL(delta.FIRMA_NAVN_2,'null') AND
IFNULL(main.SE_NR,0) = IFNULL(delta.SE_NR,0) AND IFNULL(main.KONCERN_NR,'null') = IFNULL(delta.KONCERN_NR,'null') AND
IFNULL(main.FIRMA_STATUS_DATO,'null') = IFNULL(delta.FIRMA_STATUS_DATO,'null') AND
IFNULL(main.FIRMA_STATUS_DATO_NUL,'null')= IFNULL(delta.FIRMA_STATUS_DATO_NUL,'null') AND
IFNULL(main.FIRMA_STATUS,'null') = IFNULL(delta.FIRMA_STATUS,'null') AND IFNULL(main.FIRMA_TYPE,'null') = IFNULL(delta.FIRMA_TYPE,'null') AND IFNULL(main.ANTAL_ANSAT_DATO,'null') = IFNULL(delta.ANTAL_ANSAT_DATO,'null') AND IFNULL(main.ANTAL_ANSAT_DATO_NUL,'null') = IFNULL(delta.ANTAL_ANSAT_DATO_NUL,'null') AND IFNULL(main.ANTAL_ANSAT,0) = IFNULL(delta.ANTAL_ANSAT,0) AND IFNULL(main.ANTAL_ANSAT_NUL,'null') = IFNULL(delta.ANTAL_ANSAT_NUL,'null') AND IFNULL(main.ANTAL_ANSAT_KILDE,'null') = IFNULL(delta.ANTAL_ANSAT_KILDE,'null') AND IFNULL(main.FIRMA_STIFTET_DATO,'null') = IFNULL(delta.FIRMA_STIFTET_DATO,'null') AND IFNULL(main.FIRMA_STIFTET_DATO_NUL,'null') = IFNULL(delta.FIRMA_STIFTET_DATO_NUL,'null') AND IFNULL(main.AS_REGISTRERET,0) = IFNULL(delta.AS_REGISTRERET,0) AND IFNULL(main.MOMS_REGISTRERET,'null') = IFNULL(delta.MOMS_REGISTRERET,'null') AND IFNULL(main.FAGLIG_FORENING,'null') = IFNULL(delta.FAGLIG_FORENING,'null') AND IFNULL(main.HEKTAR,0) = IFNULL(delta.HEKTAR,0) AND IFNULL(main.RET_SBH,'null') = IFNULL(delta.RET_SBH,'null') AND IFNULL(main.RET_TIMESTAMP,'null') = IFNULL(delta.RET_TIMESTAMP,'null') AND IFNULL(main.SUPL_FIRMA_NAVN,'null') = IFNULL(delta.SUPL_FIRMA_NAVN,'null') AND IFNULL(main.SUPL_FIRMA_NAVN_NUL,'null') = IFNULL(delta.SUPL_FIRMA_NAVN_NUL,'null') AND IFNULL(main.CVR_NR,0) = IFNULL(delta.CVR_NR,0) AND IFNULL(main.P_NR,0) = IFNULL(delta.P_NR,0)
WHEN NOT MATCHED BY SOURCE AND main.SystemTimeEnd = "5999-12-31 23:59:59" --Close all updated records
THEN UPDATE SET SystemTimeEnd=delta.SystemTime, current_timestamp, LastAction = 'U'
WHEN NOT MATCHED BY TARGET --insert all new and updated records
THEN INSERT (FF_NR, FIRMA_PRODENH_TYPE, FIRMA_NAVN_1, FIRMA_NAVN_2, SE_NR, KONCERN_NR, FIRMA_STATUS_DATO, FIRMA_STATUS_DATO_NUL, FIRMA_STATUS, FIRMA_TYPE, ANTAL_ANSAT_DATO, ANTAL_ANSAT_DATO_NUL, ANTAL_ANSAT, ANTAL_ANSAT_NUL, ANTAL_ANSAT_KILDE, FIRMA_STIFTET_DATO, FIRMA_STIFTET_DATO_NUL, AS_REGISTRERET, MOMS_REGISTRERET, FAGLIG_FORENING, HEKTAR, RET_SBH, RET_TIMESTAMP, SUPL_FIRMA_NAVN, SUPL_FIRMA_NAVN_NUL, CVR_NR, P_NR, SystemTime, SystemTimeEnd, Lastupdated, LastAction)
VALUES (delta.FF_NR, delta.FIRMA_PRODENH_TYPE, delta.FIRMA_NAVN_1, delta.FIRMA_NAVN_2, delta.SE_NR, delta.KONCERN_NR, delta.FIRMA_STATUS_DATO, delta.FIRMA_STATUS_DATO_NUL, delta.FIRMA_STATUS, delta.FIRMA_TYPE, delta.ANTAL_ANSAT_DATO, delta.ANTAL_ANSAT_DATO_NUL, delta.ANTAL_ANSAT, delta.ANTAL_ANSAT_NUL, delta.ANTAL_ANSAT_KILDE, delta.FIRMA_STIFTET_DATO, delta.FIRMA_STIFTET_DATO_NUL, delta.AS_REGISTRERET, delta.MOMS_REGISTRERET, delta.FAGLIG_FORENING, delta.HEKTAR, delta.RET_SBH, delta.RET_TIMESTAMP, delta.SUPL_FIRMA_NAVN, delta.SUPL_FIRMA_NAVN_NUL, delta.CVR_NR, delta.P_NR, delta.SystemTime, '5999-12-31 23:59:59', current_timestamp, 'I')
Can anybody tell me if there are any downsides to this approach? Considering BigQuery doesn't deal with primary keys at all, it would be nice to lose the concept completely for us in the solution.
As per my understanding this should not be a problem. Unless your conditions acts on columns which values are not unique, while you have the need to update only that particular datapoint.

ORA-38104 when trying to update my table using merge

I have a stored procedure in which I want to update some columns, so I wrote below code:
PROCEDURE UPDATE_MST_INFO_BKC (
P_SAPID IN NVARCHAR2
) AS
BEGIN
MERGE INTO tbl_ipcolo_billing_mst I
USING (
SELECT
R4G_STATE, -- poilitical state name
R4G_STATECODE, -- poilitical state code
CIRCLE, -- city name
NE_ID,
LATITUDE,
LONGITUDE,
SAP_ID
FROM
R4G_OSP.ENODEB
WHERE
SAP_ID = P_SAPID
AND ROWNUM = 1
)
O ON ( I.SAP_ID = O.SAP_ID )
WHEN MATCHED THEN
UPDATE SET I.POLITICAL_STATE_NAME = O.R4G_STATE,
I.POLITICAL_STATE_CODE = O.R4G_STATECODE,
I.CITY_NAME = O.CIRCLE,
I.NEID = O.NE_ID,
I.FACILITY_LATITUDE = O.LATITUDE,
I.FACILITY_LONGITUDE = O.LONGITUDE,
I.SAP_ID = O.SAP_ID;
END UPDATE_MST_INFO_BKC;
But it is giving me error as
ORA-38104: Columns referenced in the ON Clause cannot be updated: "I"."SAP_ID"
What am I doing wrong?
You are joining the source to destination tables on I.SAP_ID = O.SAP_ID and then, when matched, are trying to update them and set I.SAP_ID = O.SAP_ID. You cannot update the columns used in the join ... and why would you want to as you have already determined that the values are equal.
Just remove the last line of the UPDATE:
...
O ON ( I.SAP_ID = O.SAP_ID )
WHEN MATCHED THEN
UPDATE SET I.POLITICAL_STATE_NAME = O.R4G_STATE,
I.POLITICAL_STATE_CODE = O.R4G_STATECODE,
I.CITY_NAME = O.CIRCLE,
I.NEID = O.NE_ID,
I.FACILITY_LATITUDE = O.LATITUDE,
I.FACILITY_LONGITUDE = O.LONGITUDE;
The error message tells you what the problem is - a MERGE statement cannot update the columns used in the ON clause - and even tells you what column is the problem: "I"."SAP_ID".
So Oracle hurls ORA-38104 because of this line in your WHEN MATCHED branch
I.SAP_ID = O.SAP_ID;
Remove it and your problem disappears. Fortunately the line is unnecessary: I.SAP_ID already equals O.SAP_ID, otherwise the record wouldn't go down the MATCHED branch.
The reason why is quite straightforward: transactional consistency. The MERGE statement operates over a set of records defined by the USING clause and the ON clause. Updating the columns used in the ON clause threatens the integrity of that set, and so Oracle forbids it.

Update columns In a Teradata Table using data from another table

I have seen many posts and followed the syntax to write the below query. But it is still given the error "Column/Parameter wm_ad_hoc.temp.temp does not exist"
Please assist in figuring out what am I doing wrong here.
UPDATE temp
FROM wm_ad_hoc.OWNED_ITEM_STORE_DLY temp,
wm_ad_hoc.OWNED_ITEM_STORE_DLY_UTIL util
SET temp.VENDOR_STOCK_ID = util. VENDOR_STOCK_ID,
temp.ON_HAND_EACH_QTY = util. ON_HAND_EACH_QTY,
temp.VENDOR_STOCK_ID = util.VENDOR_STOCK_ID
WHERE temp. VENDOR_NBR = util. VENDOR_NBR
AND temp.WMI_ITEM_NBR = util.WMI_ITEM_NBR
AND temp. store_nbr = util. store_nbr
AND temp.BUSINESS_DATE = util.BUSINESS_DATE
You need to not qualify your SET columns. So:
UPDATE temp
FROM wm_ad_hoc.OWNED_ITEM_STORE_DLY temp,
wm_ad_hoc.OWNED_ITEM_STORE_DLY_UTIL util
SET VENDOR_STOCK_ID = util.VENDOR_STOCK_ID,
ON_HAND_EACH_QTY = util.ON_HAND_EACH_QTY,
VENDOR_STOCK_ID = util.VENDOR_STOCK_ID
...

ORA-02070: database INVWARE does not support in this context

Background:
I am wanting to run this merge inside a procedure on a schedule. I have to insert new data into the sql database table and if the data exist, I am wanting to update the quantities.
Problem:
I am trying to do a merge from an Oracle database to a sql database and getting an error (see the title of this question). I have tried using the merge with the same sql script used to create the view and it returned the same error.
Question:
Is the problem something in my code (see below)?
MERGE INTO "receipt_details"#invware d
USING (
SELECT *
FROM raf_po_receiving_details_v
WHERE last_update_date >= '1-AUG-2013' ) s
ON ( "po_header_id" = s.po_header_id
and "po_line_id" = s.po_line_id )
WHEN MATCHED THEN UPDATE
SET "purchased_qty" = s.purchased_qty,
"qty_received" = s.qty_received,
"balance_remaining" = s.balance_remaining,
"qty_billed" = s.qty_billed
WHEN NOT MATCHED THEN INSERT ( "warehouse_code", "po_number", "po_header_id",
"vendor_name", "line_num",
"item_code", "purchased_qty", "qty_received",
"rcv_by", "balance_remaining", "qty_billed",
"closed_code", "rec_date", "need_by_date", "po_line_id" )
VALUES (s.warehouse_code, s.po_number, s.po_header_id, s.vendor_name,
s.line_num, s.item_code, s.purchased_qty, s.qty_received, s.rcv_by,
s.balance_remaining, s.qty_billed, s.closed_code, s.rec_date, s.need_by_date, po_line_id);

How do I update multiple columns with a subquery in a single statement?

I am attempting to update a temp table from a source table:
UPDATE #DETAIL
SET EXCD_ID, CDOR_OR_AMT, CDOR_OR_VALUE
(SELECT
CDID_ADDL_DATA_1, CDID_ADDL_DATA, CDID_VALUE_STRING
FROM
CMC_CDID_DATA CDID
WHERE
CDID.CLCL_ID = DTL.CLCL_ID AND
CDID.CDML_SEQ_NO = DTL.CDML_SEQ_NO AND
CDID_TYPE = 'NDC'
)
FROM #DETAIL DTL
WHERE DTL.CDOR_OR_ID = 'XS'
Unfortunately it complains
Incorrect syntax near ',' (on the '(SELECT' line)
Incorrect syntax near 'FROM' (the second one)
After much trial and error I pooled some help at work and we came up with this:
UPDATE #DETAIL
SET DTL.EXCD_ID = CDID.CDID_ADDL_DATA_1,
DTL.CDOR_OR_AMT = CONVERT(MONEY,CDID.CDID_ADDL_DATA),
DTL.CDOR_OR_VALUE = CDID.CDID_VALUE_STRING
FROM #DETAIL DTL
INNER JOIN
CMC_CDID_DATA CDID ON
CDID.CLCL_ID = DTL.CLCL_ID AND
CDID.CDML_SEQ_NO = DTL.CDML_SEQ_NO
WHERE DTL.CDOR_OR_ID = 'XS'
AND CDID.CDID_TYPE = 'NDC'
Which sybase seems to accept.
You have to make the update like this:
UPDATE #DETAIL
SET DTL.EXCD_ID = CDID.CDID_ADDL_DATA_1,
DTL.CDOR_OR_AMT = CDID.CDID_ADDL_DATA
DTL.CDOR_OR_VALUE = CDID.CDID_VALUE_STRING
FROM #DETAIL DTL
INNER JOIN (SELECT
CDID_ADDL_DATA_1, CDID_ADDL_DATA, CDID_VALUE_STRING
FROM
CMC_CDID_DATA ) CDID ON CDID.CLCL_ID = DTL.CLCL_ID AND
CDID.CDML_SEQ_NO = DTL.CDML_SEQ_NO AND
CDID_TYPE = 'NDC'
WHERE DTL.CDOR_OR_ID = 'XS'
Check THIS ARTICLE for more info!
I just tried this out and it worked (on Oracle)
update dstTable T
set (T.field1, T.field2, T.field3) =
(select S.value1, S.value2, S.value3
from srcTable S
where S.key = T.Key);
This, unfortunately is Oracle specific syntax.
Caveat: Note that the update above has no where clause. It updates the entire table. If the subquery return no rows then the target fields are set to NULL. Also, it's an error if the subquery returns more than one row.