MERGE with Oracle Stored Procedure - PL/SQL - sql

I am trying to insert a records into my table CAT_BOM_ITEM from table TMP_BOM_STEEL8. If there are new records from the source I want the target table to be updated.
I have a procedure created and I am using a merge query inside it.
create or replace PROCEDURE SP_LOAD_CAT_BOM_MATERIALS AS
BEGIN
DELETE FROM BI_ODS.CAT_BOM_ITEM;
INSERT INTO BI_ODS.CAT_BOM_ITEM
(
ID_BOM_ITEM,
PT_PART,
PS_COMP,
IPD_PART,
SEPARADOR,
ORG_ID,
DB_ID,
LOADDATE
)
SELECT
A.ID_SEPARADOR,
A.PT_PART,
A.PS_COMP,
A.IPD_PART,
A.SEPARADOR,
A.ORG_ID,
A.DB_ID,
A.LOADDATE
FROM TMP_BOM_STEEL8 A;
COMMIT;
EXECUTE IMMEDIATE
'
Merge Into BI_ODS.CAT_BOM_ITEM B
USING
(SELECT
ID_SEPARADOR,
PT_PART,
PS_COMP,
IPD_PART,
SEPARADOR,
ORG_ID,
DB_ID,
LOADDATE
FROM TMP_BOM_STEEL8 ) A
ON (A.ID_SEPARADOR = B.ID_BOM_ITEM
AND A.DB_ID = B.DB_ID
AND A.ORG_ID = B.ORG_ID)
WHEN MATCHED THEN UPDATE SET
A.PT_PART = B.PT_PART
A.PS_COMP= B.PS_COMP
A.IPD_PART= B.IPD_PART
A.SEPARADOR = B.SEPARADOR
WHEN NOT MATCHED THEN INSERT (
ID_SEPARADOR,
PT_PART,
PS_COMP,
IPD_PART,
SEPARADOR,
ORG_ID,
DB_ID,
LOADDATE)
VALUES (
A.ID_SEPARADOR,
A.PT_PART,
A.PS_COMP,
A.IPD_PART,
A.SEPARADOR,
A.ORG_ID,
A.DB_ID,
A.LOADDATE);
';
COMMIT;
When i compile the procedure this is the error:
ORA-00933: SQL command not properly ended
ORA-06512: at "BI_ODS.SP_LOAD_CAT_BOM_MATERIALS", line 28
ORA-06512: at line 2
Can someone help in solving this.
Thanks in advance.

You need to:
Remove the ; semi-colon from the end of the string you are passing to EXECUTE IMMEDIATE;
Add commas at the end of each assignment in the UPDATE clause of the MERGE statement;
Swap the left- and right-terms in the UPDATE assignments as you are updating B from A (rather than vice versa);
Change INSERT ( ID_SEPARADOR, to INSERT ( ID_BOM_ITEM,; and
Add END; to terminate the stored procedure.
You also don't need to use EXECUTE IMMEDIATE and you shouldn't COMMIT in a stored procedure (as it prevents you from being able to ROLLBACK multiple statements; instead, use COMMIT in the PL/SQL block you are using to call the procedure that way you can control when the COMMIT occurs and can chain several procedures together and potentially roll them all back if needed).
db<>fiddle here

You should have an END statement at the end of your procedure, but I don't see anything else particularly wrong. You don't need EXECUTE IMMEDIATE and that seems to be the line it's objecting to, so try removing that.

Related

SQL command not ended properly at pkg_test

I have to write a stored procedure that starts copying the data from a table 'company' into a staging table 'company_stg' if no records for that date are present in it.
I have the following code :
CREATE OR REPLACE
PACKAGE BODY PKG_TEST AS
PROCEDURE SP_BILLING AS
BEGIN
EXECUTE IMMEDIATE 'SELECT * FROM COMPANY INTO COMPANY_STG
WHERE NOT EXISTS (SELECT * FROM COMPANY_STG WHERE AS_OF_DATE = "2023-02-08")';
END;
END PKG_TEST;
I AM GETTING THE ERROR "SQL COMMAND NOT PROPERLY ENDED"
company * company_stg have as_of_date as a column. rest all are same.
please help me with this
I have also tried
if not exists (SELECT * FROM COMPANY_STG WHERE AS_OF_DATE = "2023-02-08")
then
select from company into company_stg
So many things look bad in that piece of code...
First, why use dynamic SQL execute immediate? It's best to avoid dynamic SQL as much as possible because it leads to runtime errors and requires pretty much instrumentation so that it may be debugged. Generally you use dynamic SQL when you do not know beforehand the name of a table it will operate on, which is not the case for you. You definitely know you have to work with tables COMPANY and COMPANY_STG. Is it not so?
Then, it doesn't look like you have read the manual to see an insert select.
When you insert into a table, it's best to give the list of columns into which you actually insert data. If one alters that table and adds one or more than one column, the insert which does not have the list of columns will crash.
Thus, to insert into COMPANY_STG data from COMPANY, the SQL should look like below:
insert into company_stg(
... ---- here should be the list of columns you insert data into
)
select
... --- here should the source columns you are willing to insert
from company c
where not exists (
select 1
from company_stg cs
where cs.as_of_date= --- what is the condition??? I did not understand
)
;
You have not given the structures for those tables, so that I can't give you the columns to select and to insert into. Nor did I really understand what the condition for inserting data should be.
SELECT does not perform a copy and SELECT * FROM COMPANY INTO COMPANY_STG is not valid syntax. You want to use an INSERT statement to do that (and check if there is any row first):
CREATE OR REPLACE PACKAGE BODY PKG_TEST AS
PROCEDURE SP_BILLING
AS
BEGIN
DECLARE
v_staged_count NUMBER;
BEGIN
SELECT 1
INTO v_staged_count
FROM COMPANY_STG
WHERE AS_OF_DATE = DATE '2023-02-08'
FETCH FIRST ROW ONLY; -- We don't care how many rows so stop after finding
-- the first one.
-- Stop as rows have been found.
RETURN;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Continue
NULL;
END;
INSERT INTO company_stg
SELECT *
FROM COMPANY;
END;
END PKG_TEST;
/
fiddle

Oracle Trigger with INSERT INTO table

I am trying to create an Oracle Trigger with After Update statement. It appears that I cannot create the trigger because I get prompted with Table does not exist error:
CREATE OR REPLACE TRIGGER USR1.CORE_FINISHED
AFTER UPDATE OF RUNTIME_STATUS
ON USR2.runtime_btc
FOR EACH ROW
WHEN ( new.RUNTIME_STATUS = 'FINISHED'
AND new.id = 'cr_daily')
BEGIN
INSERT INTO USR1.test_dq_log (BTC_NO, TBL_NAM, SCR_NAM)
SELECT BTC_NO, TBL_NAM, SCR_NAM
FROM USR2.EVENT_LG -- This is where the script is throwing errors
WHERE BTC_NO = :NEW.BTC_NO;
END;
What is weird is that the same expression when run outside of the trigger:
INSERT INTO USR1.test_dq_log (BTC_NO, TBL_NAM, SCR_NAM)
SELECT BTC_NO, TBL_NAM, SCR_NAM
FROM USR2.EVENT_LG
WHERE BTC_NO = 'any number here'
Seems to work smoothly and it inserts rows in the table!
It looks as if you were granted privileges to insert into that table via role. Were you?
If so, it works at SQL level or anonymous PL/SQL blocks, but won't work in named PL/SQL procedures (e.g. stored procedures, functions, triggers) - you'll have to acquire that grant directly (not via role).

Oracle Trigger - update table B based on a condition of table A

I'm attempting to create an Oracle trigger which sets the values of a column on table B based on a select statement run within the trigger.
I want to be able to set the values of the 'is_active' column in table B to 'N' based on the select statement after an insert on table A has been executed.
My query is as follows:
CREATE OR REPLACE TRIGGER INACTIVE_STATE
AFTER INSERT ON
COMMENTS
FOR EACH ROW
DECLARE
inactive_id number;
BEGIN
SELECT distinct b.id
into inactive_id
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
update modules
set is_active='N'
where ID=:inactive_id
END INACTIVE_STATE;
/
When I try and complpile this trigger, I get the following errors:
Error(15,1): PL/SQL: SQL Statement ignored
Error(17,10): PLS-00049: bad bind variable 'INACTIVE_ID'
Error(17,15): PL/SQL: ORA-00933: SQL command not properly ended
Error(19,1): PLS-00103: Encountered the symbol "/" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with <an identifier> <a double-quoted delimited-identifier> <a bind variable> << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
It seems it doesn't like the update statement, or the bind variable isn't being parsed within this process.
If I seperate these statements into 2 commands (using var to handle the bind variable :inactive_id) it works as expected.
VAR INACTIVE_ID number
begin
select distinct b.id into :INACTIVE_ID
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
end;
/
PL/SQL procedure successfully completed.
SQL>
SQL> update modules
set is_active='N'
where ID=:INACTIVE_ID
/
1 row updated.
Any ideas what I might be overlooking?
As Tony Andrews pointed out in the comments of the original post, I was incorrectly using a colon before the "inactive_id" variable in the where clause.
The correct code should have been:
CREATE OR REPLACE TRIGGER INACTIVE_STATE
AFTER INSERT ON
COMMENTS
DECLARE
inactive_id number;
BEGIN
SELECT distinct b.id
into inactive_id
from comments a,
modules b
where a.module_name=b.name
and a.type_id='FUNCTIONAL'
and a.module_id=b.id;
update modules
set is_active='N'
where ID=inactive_id
END INACTIVE_STATE;
/
Try using
PRAGMA AUTONOMOUS_TRANSACTION;
before begin

Stored procedure to perform different tasks?

I have a customer table.
I have created stored procedure I can use to insert new data into the table. But what if I wanted to use the same procedure to update OR delete data from that table. Could I do this easily or do I have to use a separate function/procedure for each function?
create or replace procedure add_customer(custid in table.id%type,
name table.name%type)
is
begin
insert into table(id, name)
values(id, name);
commit;
end;
/
You can add parameter like action in example below and use it in code:
create or replace procedure modify_customer(
action in varchar2, custid in table.id%type, custname table.name%type)
is
begin
if action = 'insert' then
insert into table(id, name) values(custid, name);
commit;
elsif action = 'delete' then
delete from table where id = custid and name = custname;
commit;
end if;
end;
You can add a discriminator parameter to your add_customer procedure which says whether the action is INSERT, UPDATE or DELETE. Based on this parameter you can create the required insert, update or delete statement. This way you will be able to use a common procedure for all the actions.
As far as using one procedure or multiple procedures, if the table is a simple one with limited number of columns, one procedure should do fine. But once the number of columns increases in the table, one procedure might become more complicated than required.

use insert statement into a procedure !

Can i use insert into tables in a procedure (on oracle) ? example:
procedure my_procedure (aa1 number ,aa2 number ) is
begin
insert into lam_table values(aa1,aa2,null) ;(*ofcourse depending on the tables )
...
...
end ;
** note
i tried it and it worked but there were a message in the bottom that said (successfully compiled not modified )
Yes, you can. Just be aware of the difference between creating the procedure and executing it. Once the procedure is created, you can execute it with:
begin
my_procedure(aa1, aa2);
end;
where aa1 and aa2 are the supplied values for the args.
Just as dpbradley says.
Also, any insert performed by your insert statement will only be visible in that session unless you do a
commit;