How can i update the old row value. I have used the :old variable but it does not work for me. Herein my code, if PID already exists in project table i.e. (varProjectExists = 1) then i update the payment term. Now, if payment term is null , i want to update with previous paymnent term value else new payment term that is flown
CREATE OR REPLACE TRIGGER TRIG_PROJECT_INSERT AFTER
INSERT ON TEST_SYN_EAI_PROJECT_IN FOR EACH row DECLARE varError_Msg NVARCHAR2(100);
varSucceeded NVARCHAR2(1);
varActive_YN NVARCHAR2(50);
varProject_Id INT;
varPid INT ;
varPay_Term VARCHAR2(200);
varPay_Term1 VARCHAR2(200);
varError_id INT;
varCurr_activeyn INT;
varProjectExists NUMBER;
BEGIN
varError_Msg := 'No error';
varSucceeded := 'Y';
varError_id := 0;
varProjectExists := 0;
varPID := :new.pid;
varPay_Term := :new.ATTRIBUTE1;
varPay_Term1 := :old.ATTRIBUTE1;
varActive_YN := :new.active_yn;
varProject_ID := :new.project_id;
IF (NVL(varProject_Id,0) = 0 ) THEN
varError_Msg := 'project ID can not be null';
varSucceeded := 'N';
varError_id := 1;
END IF;
SELECT
CASE
WHEN (UPPER(varActive_YN) = 'ACTIVE'
OR UPPER(varActive_YN) = 'Y')
THEN 1
WHEN (UPPER(varActive_YN) = 'INACTIVE'
OR UPPER(varActive_YN) = 'N')
THEN 0
ELSE varcurr_activeyn
END
INTO varActive_YN
FROM Dual;
SELECT COUNT(1)
INTO varProjectExists
FROM project
WHERE ProjectUniversalID = varProject_ID;
IF (varProjectExists = 1) THEN
UPDATE project
SET PID = varPID,
PAYMENTTERM =
CASE
WHEN varPay_Term = 'NULL'
THEN varPay_Term1
WHEN varPay_Term IS NULL
THEN varPay_Term1
ELSE varPay_Term
END
ELSE .....
The OLD pseudo-record refers to the pre-update state of the current row, and so is only meaningful during an update or delete operation. If you're inserting a new record there is no 'old' state to refer to.
From your comment you want to use the previous payment term value that already exists in the PROJECT table. That would't be available in OLD anyway as it isn't the table the trigger is against. If that is the case then you need to retrieve it at the same time you check that it exists. Something like:
CREATE OR REPLACE TRIGGER TRIG_PROJECT_INSERT
AFTER INSERT ON TEST_SYN_EAI_PROJECT_IN
FOR EACH ROW
DECLARE
...
BEGIN
...
varPay_Term := :new.ATTRIBUTE1;
-- varPay_Term1 := :old.ATTRIBUTE1; -- not valid
varActive_YN := :new.active_yn;
...
-- this doesn't need to select from dual, you can assign directly
varCurr_activeyn := CASE
WHEN (UPPER(varActive_YN) = 'ACTIVE'
OR UPPER(varActive_YN) = 'Y')
THEN 1
WHEN (UPPER(varActive_YN) = 'INACTIVE'
OR UPPER(varActive_YN) = 'N')
THEN 0
ELSE varCurr_activeyn
END CASE;
-- get the current term as you check if a record exists
SELECT COUNT(1), MAX(PAYMENTTERM)
INTO varProjectExists, varPay_Term1
FROM project
WHERE ProjectUniversalID = varProject_ID;
IF (varProjectExists = 1) THEN
UPDATE project
SET PID = varPID,
PAYMENTTERM =
CASE
WHEN varPay_Term = 'NULL'
THEN varPay_Term1
WHEN varPay_Term IS NULL
THEN varPay_Term1
ELSE varPay_Term
END
...
The MAX() is needed because you're already using an aggregate COUNT(), to avoid a no-data-found error if the ID doesn't exist. Assuming that ID is unique, it doesn't matter if you use MAX or MIN, the result is the same. (If it isn't unique then you'd have to decide which term value to use, so it's probably a safe assumption).
Related
I have a Validation beginner's question here:
I have a select list item, options:
Product
SKU
If the user selects the SKU option a new textfield Item is shown for the customer to write down the SKU number.
My validation then tries to prevent an invalid SKU to be inserted.
This is what I have so far:
declare
v_rows_approved_min number;
v_rows_approved_max number;
err varchar2(300);
begin
if :P8_PRODUCT_OR_SKU = 'SKU' -- THIS IS MY SELECT LIST ITEM
then
err := 'Not a valid SKU';
v_rows_approved_min := 1;
select count(*) into v_rows_approved_max from SKU_TABLE;
for cur_a in (select SKU from SKU_TABLE)
loop
exit when v_rows_approved_min > v_rows_approved_max;
if :P8_SKU = cur_a.SKU
then return err;
else null;
end if;
v_rows_approved_min := v_rows_approved_min + 1;
end loop;
else null;
end if;
end;
Not sure what's going on here, can anyone help please?
Thanks!
This code is overly complex. It checks if a page item value exists in a table and returns an error if a match is found. This is done with a loop with some additional logic to exit the loop with the number of iterations reaches the select count. That last logic is not needed. If a table contains 5 rows, then the loop will have 5 iterations. No need to do a SELECT count from the table (v_rows_approved_max) and then check every iteration if that number has not been reached yet...
Also, there is no RETURN statement if not match is found, so that is added at the end.
Here is an attempt at a rewrite:
DECLARE
---- not needed
-- v_rows_approved_min NUMBER;
-- v_rows_approved_max NUMBER;
err VARCHAR2(300) := 'Not a valid SKU';
BEGIN
IF :P8_PRODUCT_OR_SKU = 'SKU' -- THIS IS MY SELECT LIST ITEM
THEN
---- err can be defaulted in declaration
--err := 'Not a valid SKU';
--v_rows_approved_min := 1;
---- not needed see below
--SELECT COUNT(*) INTO v_rows_approved_max FROM sku_table;
FOR cur_a IN ( SELECT sku FROM sku_table ) LOOP
---- not needed. You're looping through the table, v_rows_approved_min will be > than v_rows_approved_max
--EXIT WHEN v_rows_approved_min > v_rows_approved_max;
IF :P8_SKU = cur_a.sku THEN
RETURN err;
---- not needed
-- ELSE
-- NULL;
END IF;
---- not needed
-- v_rows_approved_min := v_rows_approved_min + 1;
END LOOP;
---- not needed
-- ELSE
-- NULL;
END IF;
-- you need to return something whenever the function ends...
RETURN NULL;
END;
/
however...
This can be greatly simplified.
Create a validation of type "Rows returned"
Source:
SELECT
1
FROM
sku_table WHERE sku = :P8_SKU
Error Message: Not a valid SKU
Server Side condition (Type Item = Value): Item: P8_PRODUCT_OR_SKU; Value: SKU
This does exactly the same thing.
I have a cursor that I need to loop through and check a couple conditions. If the person's category is greater than 24, I need it to then see if the person also had a previous record in the table and change the value of the v_check variable if it meets the conditions. The problem is it is saying I can't use a query here. How could I pull this off?
[Error] Compilation (248: 39): PLS-00405: subquery not allowed in this context
IF v_category > 24 THEN
IF v_person_id = (
SELECT person_id
FROM mytable
WHERE category = 24
AND outcome IS NULL
AND person_id = v_person_id
) THEN v_check := 'NO PREV OUTCOME';
ELSIF v_person_id = (
SELECT person_id
FROM mytable
WHERE category = 24
AND outcome_date IS NULL
AND person_id = v_person_id
) THEN v_check := 'NO OUTCOME DATE';
ELSIF category = 24 THEN v_check := 'N/A';
END IF;
ELSE v_check := 'OK';
END IF;
Main problem is that you can't use a subquery that way; first find value(s) returned by the select statement(s), then use them in case.
Also, code you posted doesn't make much sense as
you use v_person_id as a variable (to store a value into) and - at the same time - in select's where clause
the first if checks whether v_category > 24, while the last elsif says that if v_category = 24 then 'N/A' - that's never going to happen.
Here's an example which show how to do it; it probably won't work because I don't know what exactly you want to do (because of notes I posted above), but I hope that it'll get you started.
declare
v_person_id number := some value
l_person_id number;
l_person_uid number;
begin
select person_id, person_uid
into l_person_id, l_person_uid
from mytable
where category = 24
and outcome is null
and person_id = v_person_id;
v_check = case when v_category > 24 and v_person_id = l_person_id then 'NO PREV OUTCOME'
when v_category > 24 and v_person_id = l_person_uid then 'NO OUTCOME DATE'
else 'N/A'
end;
end;
I am a beginner using PL/SQL in oracle database, I just want to develop the coding that has been exist before. I want to put new condition and statement IF THEN ELSE NESTED after this code ELSIF updating THEN H_TYP := 'U';
But I am stuck with the my code, I have not yet find the way to fixing my code.
Here is my code;
create or replace TRIGGER TMCI_SUB_ITEM_MASTER_TR_R
AFTER DELETE OR INSERT OR UPDATE ON TMCI_SUB_ITEM_MASTER
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
DECLARE
H_ID TMCI_SUB_ITEM_MASTER_R.HST_ID%TYPE;
H_TYP TMCI_SUB_ITEM_MASTER_R.TSK%TYPE;
rdate DATE;
t_max_rev TMCI_SUB_ITEM_MASTER_R.REV%TYPE;
H_INS_USR_ID TMCI_SUB_ITEM_MASTER_R.INS_USR_ID%TYPE;
BEGIN
rdate := SYSDATE;
IF INSERTING THEN H_TYP := 'I';
...
ELSIF updating THEN H_TYP := 'U';
IF H_INS_USR_ID = 'SL01' THEN
SELECT NVL(MAX(Rev), 0) INTO t_max_rev FROM TMCI_SUB_ITEM_MASTER_R WHERE ITM_CD = :old.ITM_CD;
INSERT INTO TMCI_SUB_ITEM_MASTER_R
VALUES( H_ID,
H_TYP,
:old.ITM_CD,
CASE WHEN :old.ITM_NM <> :new.ITM_NM THEN CONCAT(:new.ITM_NM,'')
ELSE :new.ITM_NM
END);
ELSE
SELECT NVL(MAX(Rev), 0) INTO t_max_rev FROM TMCI_SUB_ITEM_MASTER_R WHERE ITM_CD = :old.ITM_CD;
INSERT INTO TMCI_SUB_ITEM_MASTER_R
VALUES( H_ID,
H_TYP,
:old.ITM_CD,
CASE WHEN :old.ITM_NM <> :new.ITM_NM THEN CONCAT(:new.ITM_NM,'*')
ELSE :new.ITM_NM
END);
END IF;
ELSIF deleting THEN H_TYP := 'D';
...
END IF;
END;
END;
If I login by SL01 result will always read in the last statement (ELSE ...). It should be execute the first statement.
I need a help for fixing this problem.
It looks like you are never setting H_INS_USR_ID to any value. I assume it is meant to the user for the incoming row? If that is the case, then
H_INS_USR_ID TMCI_SUB_ITEM_MASTER_R.INS_USR_ID%TYPE;
becomes
H_INS_USR_ID TMCI_SUB_ITEM_MASTER_R.INS_USR_ID%TYPE := :new.INS_USR_ID;
I am calling a function in oracle in an after update trigger. The Function is returning a value that is equated to perform a select and an insert operation.
The issue is when I am calling this function in the trigger it is getting terminated, that is it is not performing the corresponding insert operation. But the function is working fine when I execute it by itself. Also, if the trigger is run by removing the condition which is returned by the function, it is getting executed as expected.
Function:
CREATE OR REPLACE FUNCTION VERIFY_FINAL
(case_id IN number)
RETURN varchar2
IS
is_marked_final varchar2(4);
loop_count number(2);
cursor c1 is
SELECT sub_case_status from
cdm_master_sub_case
where master_id = (case_id);
BEGIN
is_marked_final := 'Y';
loop_count := 0;
FOR rec in c1
LOOP
IF (rec.sub_case_status = '1') THEN
is_marked_final := 'Y';
ELSIF (rec.sub_case_status = '2') THEN
is_marked_final := 'Y';
ELSE
loop_count := loop_count + 1;
END if;
END LOOP;
IF (loop_count > 0) THEN
is_marked_final := 'N';
END if;
RETURN is_marked_final;
END;
Trigger:
CREATE OR REPLACE TRIGGER CDM_MASTER_SUB_CASE_TRIGGER
AFTER UPDATE
on CDM_MASTER_SUB_CASE
FOR EACH ROW
DECLARE
check_var varchar2(4);
unique_id varchar2(100);
transaction_id number(10);
BEGIN
transaction_id := :new.MASTER_ID;
check_var := VERIFY_FINAL(transaction_id);
IF (check_var = 'Y') THEN
select UNIQUE_CUST_ID
INTO unique_id
from ASM355.cdm_matches
where MASTER_ID = :new.MASTER_ID
and rownum = 1;
INSERT INTO tracking_final_cases (MASTER_ID,unique_cust)
values (:new.master_id,unique_id);
END if;
END;
I would appreciate it if anyone can point me in the right direction.
1.) As tmrozek points out a return of 'N' will not do the associated insert. I might suggest having an ELSE to that IF that does something to indicate if that is what is happening.
2.) I would also point out that your SELECT INTO, if it does not find a corresponding value, would cause issues. You might want to do something to ensure that this trigger is failsafe, or have you considered what you want the code to do if that situation occurs? (Error out? Insert a null unique_id?)
3.) If you are looking at the results from a different session, bear in mind that the inserted tracking_final_cases will not be visible until you commit your changes in the session that called the trigger.
I don't know your table data but it is possible to your function to return 'N' so it wouldn't meet your trigger condition (check_var = 'Y').
If you run command like that:
update CDM_MASTER_SUB_CASE
set sub_case_status = 3;
you will probably get your problem.
Thanks guys for the time, it got resolved. I was querying a select statement in the function body over a table on which the corresponding trigger was created.
Not sure if this is possible, but I'm trying to see if I can convert this procedure to become a view because we've been having trouble with drives not populating the table when the procedure is run.
I'm trying to understand someone else's code and because of the cursors, I'm not even sure we can change this procedure to a view.
----------------------------------------------------------------------
--This Procedure will interface drive information on a nightly basis--
----------------------------------------------------------------------
Procedure HEMA_DRIVE_AUTO IS
v_start_date DATE := trunc(sysdate) -30;
v_end_date DATE := trunc(sysdate);
v_delete_stats_dt DATE := trunc(sysdate)-120;
v_total_registration_count NUMBER;
v_total_performed_count NUMBER;
v_total_collected_count NUMBER;
v_total_deferred_count NUMBER;
v_total_qns_count NUMBER;
v_existing_drive NUMBER;
v_existing_performed NUMBER;
v_maph_drive NUMBER;
--This Cursor will collect the initial data
cursor c_drive_info is
select dr.drive_id, dr.Start_time, dr.vehicle_id
from drives dr
--where dr.drive_id in(1605606);
where trunc(dr.start_time) between v_start_date and v_end_date;
--This Cursor will be used to decode the Donation Types
cursor c_procedure_codes is
select * from hema_donation_type_map hdt
where hdt.mobiles = 1 order by procedure_code_id;
--This Cursor will define the intentions but exclude theraputics inthe mapping
cursor c_intention is
select rsa_motivation_id,hema_intent_id from hema_intent_map
where rsa_motivation_id <> 4 order by rsa_motivation_id;
BEGIN
-- delete records older then 4 months
delete from hema_nightly h where trunc(h.drive_date) < v_delete_stats_dt;
commit;
FOR cur_drive IN c_drive_info LOOP
delete from hema_nightly where drive_id = cur_drive.drive_id;
commit;
-- Loop by motivation/intention
FOR cur_intent in c_intention LOOP
-- Loop to get the procedure code data
FOR cur_proc_code IN c_procedure_codes LOOP
v_total_registration_count := 0;
v_total_performed_count := 0;
v_total_collected_count := 0;
v_total_deferred_count := 0;
v_total_qns_count := 0;
v_maph_drive := 0;
-- get the count for all other procedures
select count(1)
into v_total_registration_count
from registration r
where r.drive_id = cur_drive.drive_id
and r.donation_type_id = cur_proc_code.donation_type_id
and r.motivation_id = cur_intent.rsa_motivation_id;
--get the deferral count
select count(unique(r.registration_id))
into v_total_deferred_count
from registration r
where r.drive_id = cur_drive.drive_id
and r.donation_type_id = cur_proc_code.donation_type_id
and r.motivation_id = cur_intent.rsa_motivation_id
and r.step_completed < 12
and exists (select rsc.registration_id
from reg_steps_completed rsc
where rsc.registration_id = r.registration_id
and rsc.collection_step_id = 99);
-- QNS count
select count(unique(r.registration_id))
into v_total_qns_count
from registration r
where r.drive_id = cur_drive.drive_id
and r.step_completed < 12
and not exists (select rsc.registration_id
from reg_steps_completed rsc
where rsc.registration_id = r.registration_id
and rsc.collection_step_id = 99)
and r.donation_type_id = cur_proc_code.donation_type_id
and r.motivation_id = cur_intent.rsa_motivation_id;
-- performed count is the difference between total registrations and total deferrals.
v_total_performed_count := v_total_registration_count -
(v_total_deferred_count +
v_total_qns_count);
-- not calulatind yield so keep count the same
v_total_collected_count := v_total_performed_count;
-- does this drive exist
select count(drive_id)
into v_existing_drive
from hema_nightly
where drive_id = cur_drive.drive_id
and procedure_id = cur_proc_code.procedure_code_id
and intent = cur_intent.hema_intent_id;
-- Is this an aph vehicle?
select count(vehicle_id)
into v_maph_drive
from vehicles
where veh_drive_type_uid = 2
and vehicle_id = cur_drive.vehicle_id;
if v_existing_drive > 0 then
update hema_nightly
set performed = performed + v_total_performed_count,
collected = collected + v_total_collected_count,
registered = registered + v_total_registration_count,
deferrals = deferrals + v_total_deferred_count,
qns = qns + v_total_qns_count,
drive_date = cur_drive.start_time,
mod_date = sysdate,
intent = cur_intent.hema_intent_id,
aph = v_maph_drive
where drive_id = cur_drive.drive_id
and procedure_id = cur_proc_code.procedure_code_id
and intent = cur_intent.hema_intent_id;
commit;
elsif v_existing_drive = 0 and v_total_registration_count > 0 then
insert into hema_nightly
(drive_id,
procedure_id,
performed,
collected,
registered,
deferrals,
qns,
drive_date,
mod_date,
intent,
aph)
values
(cur_drive.drive_id,
cur_proc_code.procedure_code_id,
v_total_performed_count,
v_total_collected_count,
v_total_registration_count,
v_total_deferred_count,
v_total_qns_count,
trunc(cur_drive.start_time),
sysdate,
cur_intent.hema_intent_id,
v_maph_drive);
commit;
end if;
v_existing_drive := 0;
end loop;
end loop;
end loop;
end hema_drive_auto;
Views don't perform DML (insert, update, delete) and they don't manage transactions with COMMIT and ROLLBACK; they only select and retrieve data.