After Insert Trigger sintax error - ORA-00922 - plsql

There is a table which i want to update each row after inserted, when the user_type of my schedule table is a "superuser". I tried to convert the table/column names to "simplify", so some names may not make sense, it's ok.
The trigger code:
CREATE OR REPLACE TRIGGER "TR_UPT_SCHEDULE"
AFTER INSERT
ON SCHEDULE
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE V_UserCode VARCHAR2(20);
BEGIN
--Find "super user" code.
SELECT UserTypeCode INTO V_UserCode FROM
(SELECT UL.User_Code,
UFT.UserTypeCode,
ROW_NUMBER() OVER (PARTITION BY UL.User_Code ORDER BY UF.UserTypeCode DESC) RN
FROM UserLogin UL
JOIN UserFunction UF
ON UL.User_Code = UF.User_Code
JOIN UserFuncType UFT
ON UFT.UserTypeCode = UF.UserTypeCode
WHERE UFT.FuncType = 'S'
) WHERE RN = 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
V_UserCode := NULL;
IF V_UserCode IS NOT NULL
THEN UPDATE SCHEDULE
SET :NEW.UserTypeCode = V_UserCode,
OrigScheType = :NEW.UserTypeCode
WHERE CompCode = :NEW.CompCode
AND UserTypeCode = :NEW.UserTypeCode
AND ScheOrigin = :NEW.ScheOrigin
AND ScheCode = :NEW.ScheCode;
END IF;
END;
When i try to create the trigger, is returned the fallowing error:
ORA-00922
I'm pretty sure the problem is the UPDATE part, where tried compare in the WHERE clause, the Schedule table keys to be sure of which row i'm updating(inserted), but i could not realize the problem.

In triggers, you don't update tablename set :new.columnname = some value, you directly assign values to :new.columnname using PL/SQL assignments or select into. Therefore, from a syntax point of view, the final update should be replaced with something like:
:new.usertypecode := v_usercode;
:new.origschetype := v_usercode;
(I am assuming the update is only intended to apply to the current row. If the idea is to update multiple rows in the triggering table, you can't do that in a row-level trigger.)
However, this logic doesn't look like it will work:
exception
when no_data_found then
v_usercode := null;
if v_usercode is not null then
v_usercode has to be null at this point, so you don't have a value to set :new.usertypecode to, and I'm not sure what you want the trigger to do.

Related

Compound Triggers

Problem - Want to avoid the problem of mutating triggers by using the compound trigger. But unable to do so
Background -
I want to insert data in new table " Tracking Table " whenever there is change in Main table "CUSTOM_ITEM"
Design is such that, everytime a row is created in table an ITEM_ID is generated but there is a column FIRST_ITEM_ID that remains same in some cases.
So whenever a new row is added, I want to check its FIRST_ITEM_ID and then check the whole table and find out all the ITEM_IDs having that same FIRST_ITEM_ID.
And I want to insert all those rows in the New table using trigger.
Is it even possible ?
Attaching the trigger :
CREATE OR REPLACE TRIGGER APP.TEST_TRG
FOR DELETE OR INSERT OR UPDATE
ON APP.CUSTOM_ITEM
COMPOUND TRIGGER
TYPE t_change_tab IS TABLE OF APP.TEST_TRG.OBJECT_ID%TYPE;
g_change_tab t_change_tab := t_change_tab();
BEFORE EACH ROW IS
BEGIN
Select item_id bulk collect into g_change_tab from CUSTOM_ITEM where first_item_id =
(Select first_item_id from CUSTOM_ITEM where item_id = :NEW.item_id);
For i in 1 .. g_change_tab.COUNT()
LOOP
g_change_tab.extend;
END LOOP;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
For i in 1 .. g_change_tab.COUNT()
LOOP
app.bc_acs_pkg.populate_TEST_TRG /* Package Inserts data */
(p_object_type => 'ITEM',
p_object_id => g_change_tab(i));
END LOOP;
g_change_tab.delete;
END AFTER STATEMENT;
END ;
/
You can do what you want just not with your current approach. Let's take a step back. What is a mutating table exception (ORA-04091). It is thrown when you attempt to access the table on which the trigger fired in a row level event, that is not permitted. Just creating a compound trigger does not remove that restriction. So in your Before Row segment the statement
Select item_id
bulk collect into g_change_tab
from CUSTOM_ITEM where first_item_id =
(Select first_item_id from CUSTOM_ITEM where item_id = :NEW.item_id);
is invalid, and results in raising ORA-04091. What you need is to just build your collection with the necessary ids. Then process them in the After statement segment.
create or replace trigger test_trg
for delete or insert or update
on custom_item
compound trigger
type t_change_tab is
table of custom_item.first_item%type;
g_change_tab t_change_tab := t_change_tab();
before each row is
l_first_item_exists boolean := false;
indx integer;
begin
indx := g_change_tab.first;
while not l_first_item_exists
and indx is not null
loop
l_first_item_exists := (g_change_tab(indx) = :new.first_item);
if not l_first_item_exists
then
indx := g_change_tab.next(indx);
end if;
end loop;
if not l_first_item_exists
then
g_change_tab.extend;
g_change_tab(g_change_tab.last) := :new.first_item;
end if;
end before each row;
after statement is
begin
for indx in g_change_tab.first .. g_change_tab.last
loop
insert into tracking_table(item_id, first_item)
select item_id, first_item
from custom_item
where first_item = g_change_tab(indx);
end loop;
end after statement;
end test_trg;
The issue here is the loops, always the slowest processing, and very bad in triggers. Below is an approach which avoids them totally. It does however require creating your type array at the schema level.
create or replace type custom_item_change_t is
table of integer ; --custom_item.first_item%type;
create or replace trigger test_trg
for insert
on custom_item
compound trigger
g_change_tab custom_item_change_t := custom_item_change_t();
before each row is
begin
g_change_tab.extend;
g_change_tab(g_change_tab.last) := :new.first_item;
end before each row;
after statement is
begin
insert into tracking_table(item_id, first_item)
select item_id, first_item
from custom_item ci
where first_item in (select distinct column_value
from table(g_change_tab)
)
and not exists
( select null
from tracking_table tt
where ci.item_id = tt.item_id
and ci.first_item = tt.first_item
);
end after statement;
end test_trg;

Plsql case is not reading properly from case

I need to read data row by row and update it based on the matching case.
My code is just reading ORBREFTND and inserting Refund from case , not reading other cases. What am i doing wrong.
DECLARE
v_tot_rows NUMBER (3);
v_eval ISG.SWR_CERT_TXN_MSG_TYPE.DESC_TX%TYPE;
CURSOR pltfm_msg_type_cur
IS
SELECT sctmt.PLTFM_MSG_TYPE_CD
FROM ISG.SWR_CERT_TXN_MSG_TYPE sctmt
JOIN ISG.FEATURE_VALUES ft_val
ON sctmt.SWR_CERT_FETR_VAL_ID = ft_val.FEATURE_VAL
JOIN ISG.FEATURE_CATEGORY ft_cat
ON ft_val.FEATURE_CAT = ft_cat.FEATURE_CAT
WHERE ft_cat.SHORT_DESCRIP = 'PLT'
AND ft_val.FEATURE_VALUE = 'Orbital';
BEGIN
FOR msg_code IN pltfm_msg_type_cur
LOOP
CASE msg_code.PLTFM_MSG_TYPE_CD
WHEN 'WAC'
THEN
v_eval := 'Authorization and Mark for Capture';
WHEN 'ORBAUTHCAP'
THEN
v_eval := 'Authorization Capture';
WHEN 'ORBREFTND'
THEN
v_eval := 'Refund';
END CASE;
UPDATE ISG.SWR_CERT_TXN_MSG_TYPE sctmt
SET sctmt.DESC_TX = v_eval;
END LOOP;
COMMIT;
v_tot_rows := SQL%ROWCOUNT;
/* Implicit Attribute %ROWCOUNT is used to find the number of rows affected by the update command */
DBMS_OUTPUT.PUT_LINE ('Total records updated : ' || v_tot_rows);
END;
You're missing a where-condition:
UPDATE ISG.SWR_CERT_TXN_MSG_TYPE sctmt
SET sctmt.DESC_TX = v_eval
WHERE sctmt.primarykey = msg_code.primarykey; -- This one is missing. Don't know your primary key..
Without the where you'll always set the value of the last cursor-object to all objects in your table ISG.SWR_CERT_TXN_MSG_TYPE.
You also need to select the key from your table:
CURSOR pltfm_msg_type_cur
IS
SELECT sctmt.PLTFM_MSG_TYPE_CD,
sctmt.PRIMARYKEY -- select key here
FROM ISG.SWR_CERT_TXN_MSG_TYPE sctmt
JOIN ISG.FEATURE_VALUES ft_val
ON sctmt.SWR_CERT_FETR_VAL_ID = ft_val.FEATURE_VAL
JOIN ISG.FEATURE_CATEGORY ft_cat
ON ft_val.FEATURE_CAT = ft_cat.FEATURE_CAT
WHERE ft_cat.SHORT_DESCRIP = 'PLT'
AND ft_val.FEATURE_VALUE = 'Orbital';

Forms 6i if record exists then update

I need to create select statement in post_insert trigger. Is it possible if yes then how?
I want to check another table records if it exists then it will update it otherwise insert as new record. Please help.
My block code is that i want to run
DECLARE
EXSIST_TYPE varchar2(50);
EXSIST_NAME varchar2(50);
EXSIST_COMPANY VARCHAR2(100);
BEGIN
SELECT PRO_TYPE, PRO_NAME, COMPANY_NAME INTO EXSIST_TYPE, EXSIST_NAME ,EXSIST_COMPANY FROM STOCK;
IF
:PURCHASE_DETAIL.PRO_TYPE <> EXSIST_TYPE AND
:PURCHASE_DETAIL.PRO_NAME <> EXSIST_NAME AND
:PURCHASE_DETAIL.COMPANY_NAME <> EXSIST_COMPANY THEN*/
IF
:PURCHASE.RADIO_TYPE = 'PURCHASE' THEN
INSERT INTO STOCK(
PRO_TYPE ,
PRO_NAME ,
COMPANY_NAME ,
QUANTITY ,
PURCHASE_RATE,
SALE_RATE ,
RACK_NUM
)
VALUES
(
:PURCHASE_DETAIL.PRO_TYPE,
:PURCHASE_DETAIL.PRO_NAME,
:PURCHASE_DETAIL.COMPANY_NAME,
:PURCHASE_DETAIL.QUANTITY,
:PURCHASE_DETAIL.PRICE,
:PURCHASE_DETAIL.SALE_PRICE,
:PURCHASE_DETAIL.RACK_NUM
);
END IF;
ELSIF
:PURCHASE_DETAIL.PRO_TYPE = EXSIST_TYPE AND
:PURCHASE_DETAIL.PRO_NAME = EXSIST_NAME AND
:PURCHASE_DETAIL.COMPANY_NAME = EXSIST_NAME THEN
IF
:PURCHASE.RADIO_TYPE = 'PURCHASE' THEN
UPDATE STOCK SET
STOCK.QUANTITY = STOCK.QUANTITY+:PURCHASE_DETAIL.QUANTITY
WHERE
STOCK.PRO_TYPE = :PURCHASE_DETAIL.PRO_TYPE AND
STOCK.PRO_NAME = :PURCHASE_DETAIL.PRO_NAME AND
STOCK.COMPANY_NAME= :PURCHASE_DETAIL.COMPANY_NAME;
ELSIF
:PURCHASE.RADIO_TYPE = 'PRCH_RETURN' THEN
UPDATE STOCK SET
STOCK.QUANTITY = STOCK.QUANTITY-:PURCHASE_DETAIL.QUANTITY
WHERE
STOCK.PRO_TYPE = :PURCHASE_DETAIL.PRO_TYPE AND
STOCK.PRO_NAME = :PURCHASE_DETAIL.PRO_NAME AND
STOCK.COMPANY_NAME = :PURCHASE_DETAIL.COMPANY_NAME;
END IF;
END IF;
END;
You never said what happened when you ran that code.
Anyway: requirement you mentioned ("if it exists then it will update it otherwise insert") looks like an excellent candidate for a MERGE statement (also called upsert, as a combination of UPdate and inSERT).
As Forms 6i is an old piece of software, I'm pretty much sure that MERGE can't directly be used there. However, if the underlying database is at least 9i, MERGE will work - create a stored procedure that contains MERGE, and pass form items' values as parameters.
Here's an example (taken from here; have a look for more examples. I'm lazy to create my own code):
MERGE INTO employees e
USING hr_records h
ON (e.id = h.emp_id)
WHEN MATCHED THEN
UPDATE SET e.address = h.address
WHEN NOT MATCHED THEN
INSERT (id, address)
VALUES (h.emp_id, h.address);

Update and insert in oracle PL/SQL along with if else condition

Please find by below code snippet :
BEGIN
IF (in_config1 IS NOT NULL OR in_config1 !='') THEN
UPDATE question_table
SET comment = in_config1
WHERE id= id
AND questionid = 1;
ELSE
INSERT INTO question_table(
tid
,questionid
,comments)
VALUES( id
, 1
, in_config1);
END IF;
END;
My requirement is to update question_table based on some condition.If update fails which would be incase if record is not present,then i need to add insert statement in the else block.
In the above code update is working. But insert statement is not getting executed.Please let me know whats wrong?
If I understand you, you need upsert statement, where you update if the record match some value, and you insert if it doesn't. The best option can serve you in this case is MERGE clause. It's efficient, flexible and readable. The following is a general script that might need minor changes based on where you are getting the values from and your tables structures.
MERGE INTO question_table a
USING (SELECT id, your_key, in_config1 FROM DUAL) b
ON (a.id = b.id)
WHEN MATCHED THEN
UPDATE question_table
SET comment = in_config1
WHEN NOT MATCHED THEN
INSERT INTO question_table(
tid
,questionid
,comments)
VALUES( id
, 1
, in_config1);
simply you can do like this use sql%notfound
BEGIN
IF (in_config1 IS NOT NULL OR in_config1 != '') THEN
UPDATE question_table
SET comment = in_config1
WHERE id = id
AND questionid = 1;
if sql%notfound then
INSERT INTO question_table (tid, questionid, comments) VALUES (id, 1, in_config1);
end if;
END IF;
exception
when others then
dbms_output.put_line(sqlerrm);
END;

PL/SQL - Inserting data using Exception

I have the following code which is not executing correctly. I have data stored in date_tmp (varchar) that includes dates and nondates. I want to move the dates in that column to date_run (date) and data that is not a date, will be moved to a comments (varchar) column. When I run the following code, the entire set of data gets moved to comments. It runs fine when I edit out the insert statement and just run the dbms_outputline line. What might I be doing incorrectly?
DECLARE
CURSOR getrow IS
SELECT a.id, a.date_tmp
FROM mycolumn a
WHERE a.id < 1300;
v_date date;
BEGIN
FOR i in getrow LOOP
BEGIN
v_date := to_date(i.date_tmp, 'mm/dd/yy');
INSERT INTO mycolumn a(a.date_run)
VALUES(i.date_tmp);
EXCEPTION
WHEN OTHERS THEN
--dbms_output.put_line(i.date_tmp);
update mycolumn a
SET a.comments = i.date_tmp
where a.id = i.id;
END;
END LOOP;
END;
You try to insert varchar i.date_tmp into a date field. Instead insert v_date.
...
INSERT INTO mycolumn a (a.date_run)
VALUES(v_date);
...
But actually your requirement is a move. That calls for an update actually. So I think what you really want to do is:
...
update mycolumn a
SET a.date_run = v_date
where a.id = i.id
...
And actually you could have a function that checks if you have a valid date or not and then you might be able to handle the whole task using a simple update statement.
create or replace function is_a_date(i_date varchar2, i_pattern varchar2)
return date
is
begin
return to_date(i_date, i_pattern);
exception
when others return null;
end is_a_date;
With that function you could write two update statements
update mycolumn
set date_run = to_date(date_tmp,'dd/mm/yy')
where is_a_date(date_tmp, 'dd/mm/yy') is not null;
update mycolumn
set comment = date_tmp
where is_a_date(date_tmp, 'dd/mm/yy') is null;
I designed the function in a way that you could use it in various ways as it returns you a date or null but no exception if the varchar does not conform to the date pattern.
You have an insert where it looks like you need an update, like you have in the exception handler. So just change it to:
v_date := to_date(i.date_tmp, 'mm/dd/yy');
update mycolumn
set date_run = v_date
where id = i.id;
or you could shorten it to:
update mycolumn
set date_run = to_date(i.date_tmp, 'mm/dd/yy')
where id = i.id;
#hol solution is the best approach for me.
Avoid always you can loops and procedures if you can do it with simple SQL statments, your code will be more faster.
Also, if you have always have a data fixed format , you can ride of the PL/SQL function is_a_date function and do it everything with SQL... but the code gets a little uglier with something like this:
update mycolumn
set date_run = to_date(date_tmp,'dd/mm/yy')
where substr(date_tmp,1,2) between '1' and '31'
and substr(date_tmp,4,2) between '1' and '12'
and substr(date_tmp,7,2) between '00' and '99';
If you need more speed in your query or you have a huge amount of data in date_tmp, as function is_a_date is deterministic (always returns the same value given the same values for X, Y,), you can create an index for it:
create index mycol_idx on mycolumn(is_a_date(date_tmp));
And when you use the function, Oracle will use your index, like in those selects:
SELECT a.id, a.date_tmp
FROM mycolumn a
WHERE a.id < 1300
and is_a_date(a.date_tmp) is not null;
SELECT a.id, a.date_tmp
FROM mycolumn a
WHERE a.id < 1300
and (is_a_date(a.date_tmp) is not null and is_a_date(a.date_tmp)>sysdate-5);

Resources