I want to pass :new from trigger to my custom oracle function,I had tried everything but failed.
Here is my code:
set serveroutput on;
create or replace function sum_evaluation_onerow(arow in t_attend_j301%rowtype)
return number
as
total number:=0;
begin
total:=trans_attend_type(arow.class12)+total;
total:=trans_attend_type(arow.class34)+total;
total:=trans_attend_type(arow.class56)+total;
total:=trans_attend_type(arow.class78)+total;
total:=trans_attend_type(arow.class90)+total;
dbms_output.put_line(total);
return total;
end;
/
create or replace trigger tg_insert_attend_j301
after insert on t_attend_j301
for each row
declare
total number;
myrow t_attend_j301%rowtype;
begin
--total:=sum_evaluation_onerow(:new);
myrow:=:new
update u_j301.t_stud_j301 set sum_evaluation=sum_evaluation where sno=:new.sno;
end;
/
What should I do?
Assign each attribute of the %ROWTYPE individually:
create or replace trigger tg_insert_attend_j301
after insert on t_attend_j301
for each row
declare
total number;
myrow t_attend_j301%rowtype;
begin
myrow.sno := :new.sno;
myrow.class12 := :new.class12;
myrow.class34 := :new.class34;
myrow.class56 := :new.class56;
myrow.class78 := :new.class78;
myrow.class90 := :new.class90;
total:=sum_evaluation_onerow(myrow);
update u_j301.t_stud_j301
set sum_evaluation=total
where sno=:new.sno;
end;
/
db<>fiddle here
Related
Create a procedure that accepts 2 parameters represented the inv_id, and the percentage increase in price. The pseudo function should first update the database with the new price then return the new price and the quantity on hand.
Create a second procedure called L4Q3 that accepts the inv_id and the percentage increase in price. The procedure will use the old procedure to display the new value of the inventory (hint: value = price X quantity on hand)
CREATE OR REPLACE PROCEDURE ex3 (p_inv_id IN NUMBER, p_change IN NUMBER,
p_new_price OUT NUMBER, p_qoh OUT NUMBER)
AS
v_new_price NUMBER(6,2);
v_qoh NUMBER(6,2);
BEGIN
UPDATE inventory
SET inv_price = (SELECT inv_price + (inv_price*(p_change/100))
FROM inventory
WHERE inv_id = p_inv_id);
COMMIT;
SELECT inv_price, inv_qoh
INTO p_new_price, p_qoh
FROM inventory
WHERE inv_id = p_inv_id;
COMMIT;
v_qoh := p_qoh;
v_new_price := p_new_price;
DBMS_OUTPUT.PUT_LINE('hello'||v_new_price);
END;
/
CREATE OR REPLACE PROCEDURE use_ex3 ( p_inv_id NUMBER, p_change NUMBER)
AS
v_new_price NUMBER(6,2);
v_qoh NUMBER(6,2);
v_value NUMBER(10,2);
BEGIN
ex3(p_inv_id, p_change, v_new_price, v_qoh);
v_value := v_new_price*v_qoh;
DBMS_OUTPUT.PUT_LINE('value is:'||v_value);
END;
/
Consider converting your procedures like those below :
SQL> set serveroutput on;
SQL> CREATE OR REPLACE PROCEDURE ex3(p_inv_id IN inventory.inv_id%type,
p_change IN NUMBER,
p_new_price OUT inventory.inv_price%type,
p_qoh OUT inventory.inv_qoh%type) AS
BEGIN
UPDATE inventory
SET inv_price = inv_price * ( 1 + (p_change / 100) )
WHERE inv_id = p_inv_id
RETURNING inv_price, inv_qoh
INTO p_new_price, p_qoh;
DBMS_OUTPUT.PUT_LINE('hello '|| p_new_price);
END;
/
SQL> CREATE OR REPLACE PROCEDURE use_ex3(p_inv_id inventory.inv_id%type, p_change NUMBER) AS
v_new_price inventory.inv_price%type;
v_qoh inventory.inv_qoh%type;
v_value NUMBER(10, 2);
BEGIN
ex3(p_inv_id, p_change, v_new_price, v_qoh);
v_value := v_new_price * v_qoh;
DBMS_OUTPUT.PUT_LINE('value is: '|| v_value);
END;
the core issue is
missing filter WHERE inv_id = p_inv_id in the UPDATE statement.
i.e. not restricted to only one inv_id
Moreover considering below matters will make your code better :
don't need a subquery for the SET clause, just an assignment needed as inv_price = inv_price * ( 1 + (p_change / 100) )
had better defining variable types for columns as
inventory.<column_name>%type
extra local variables are not needed such as v_new_price, out
parameters of the procedures such as p_new_price might be used as
the assignment targets
SELECT statement after UPDATE is not needed, using RETURNING
INTO is enough
don't forget to use set serveroutput on to print out the results
I think it's a good habit to exclude commit in individual program
units to provide transaction integrity for data consistency in the
tables which got DML. Prefer keep only one commit inside the caller
application at the end of all statements and program units.
How do I insert variable values in to a table record in an oracle procedure?
if pCount1=0 then
insert into opions(qid,otext,oflag)
(rec.pQid, rec.pOptions, rec.pCorrect);
end if;
where rec.* are the variables of the procedure
Are you looking for one of these statements (which are functionally equivalent)?
insert into opions(qid,otext,oflag)
values (rec.pQid, rec.pOptions, rec.pCorrect);
insert into opions(qid,otext,oflag)
select rec.pQid, rec.pOptions, rec.pCorrect from dual;
This assumes that rec is defined somewhere else in the stored procedure. Otherwise, you need to use the second form with rec defined in the from clause.
Try:
CREATE OR REPLACE PROCEDURE SOME_PROC AS
nQid NUMBER;
strOptions VARCHAR2(100);
strCorrect VARCHAR2(1);
BEGIN
nQid := 1;
strOptions := 1234;
strCorrect := 'Y';
INSERT INTO OPIONS(qid, otext, oflag)
VALUES (nQid, strOptions, strCorrect);
END SOME_PROC;
Best of luck.
I have the following code
CREATE OR REPLACE FUNCTION slow_function (p_in IN NUMBER)
RETURN NUMBER
AS
BEGIN
DBMS_LOCK.sleep(1);
RETURN p_in;
END;
/
CREATE OR REPLACE PACKAGE cached_lookup_api AS
FUNCTION get_cached_value (p_id IN NUMBER)
RETURN NUMBER;
PROCEDURE clear_cache;
END cached_lookup_api;
/
CREATE OR REPLACE PACKAGE BODY cached_lookup_api AS
TYPE t_tab IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
g_tab t_tab;
g_last_use DATE := SYSDATE;
g_max_cache_age NUMBER := 10/(24*60); -- 10 minutes
-- -----------------------------------------------------------------
FUNCTION get_cached_value (p_id IN NUMBER)
RETURN NUMBER AS
l_value NUMBER;
BEGIN
IF (SYSDATE - g_last_use) > g_max_cache_age THEN
-- Older than 10 minutes. Delete cache.
g_last_use := SYSDATE;
clear_cache;
END IF;
BEGIN
l_value := g_tab(p_id);
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Call function and cache data.
l_value := slow_function(p_id);
g_tab(p_id) := l_value;
END;
RETURN l_value;
END get_cached_value;
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
PROCEDURE clear_cache AS
BEGIN
g_tab.delete;
END;
-- -----------------------------------------------------------------
END cached_lookup_api;
/
I want to pass two parameters "pi_value1" and "pi_value2" both of varchar2 to the function slow_function instead of "p_in". Is is possible to cache the results with two in parameters in oracle 10g .
the above code works fine with 1 parameter.
Please any one explain?
You'd need to create a two-dimensional array type to cache the values. Something along the lines of this (omitting the cache expiration code since that isn't changing)
CREATE OR REPLACE PACKAGE BODY cached_lookup_api
AS
TYPE t_pi_value2_tbl IS TABLE OF NUMBER
INDEX BY VARCHAR2(100);
TYPE t_cache IS TABLE OF t_pi_value2_tbl
INDEX BY VARCHAR2(100);
g_cache t_cache;
FUNCTION get_cached_value( p_pi_value1 IN VARCHAR2,
p_pi_value2 IN VARCHAR2 )
IS
BEGIN
RETURN g_cache(p_pi_value1)(p_pi_value2);
EXCEPTION
WHEN no_data_found
THEN
g_cache(p_pi_value1)(p_pi_value2) := slow_function( p_pi_value1, p_pi_value2 );
RETURN g_cache(p_pi_value1)(p_pi_value2);
END;
END;
id like to call this function:
CREATE OR REPLACE PACKAGE orders_salary_manage2 AS
FUNCTION total_calc(p_order in NUMBER)
RETURN NUMBER;
END;
CREATE OR REPLACE PACKAGE BODY orders_salary_manage2 AS
tot_orders NUMBER;
FUNCTION total_calc(p_order in NUMBER)
RETURN NUMBER
IS
c_price product.unit_price%type;
c_prod_desc product.product_desc%type;
v_total_cost NUMBER := 0;
CURSOR c1 IS
SELECT product_desc, unit_price
FROM product
WHERE product_id IN (SELECT fk2_product_id
FROM order_line
WHERE fk1_order_id = p_order);
BEGIN
OPEN c1;
LOOP
FETCH c1 into c_prod_desc, c_price;
v_total_cost := v_total_cost + c_price;
EXIT WHEN c1%notfound;
END LOOP;
CLOSE c1;
return v_total_cost;
END;
from this trigger:
CREATE OR REPLACE TRIGGER trg_order_total
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
BEGIN
total_calc(v_old_order);
END;
but i keep getting this error, note there is no error number just this:
Error at line 4: PL/SQL: Statement ignored
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
BEGIN
im new to pl/sql and just not sure what is causing the problem. When a user deletes an order from the orders table the trigger should call the function to add up all the products on the order.
Thank you
(Considering your Package compiled with no errors) Use-
ret_val:= orders_salary_manage2.total_calc(v_old_order);
The ret_val must be a NUMBER since the package function total_calc returns a NUMBER. A function MUST always return its outcoume to a variable (like ret_val) depending on the type of the return value the data type of the variable must be declared.
The syntax to call Pacakaged Procedures and functions is -
<RETURN_VARIABLE> := PACKAGE_NAME.<FUNCTION_NAME>();
PACKAGE_NAME.<PROCEDURE_NAME>(); --Since Procedure never returns
Also note that if your package is in a different SCHEMA and has no PUBLIC SYNONYM then you will have to prefix the schema name like <SCHEMA>.PACKAGE_NAME.<FUNCTION_NAME>() (considering the calling schema has execute permissions on the package).
So,
CREATE OR REPLACE TRIGGER trg_order_total
BEFORE DELETE ON placed_order
FOR EACH ROW
DECLARE
v_old_order NUMBER := :old.order_id;
v_ret_val NUMBER := 0;
BEGIN
v_ret_val := orders_salary_manage2.total_calc(v_old_order);
--...Do stuff with v_ret_val
END;
I have a statement level trigger that fires whenever INSERT UPDATE or DELETE operations are performed on a table (called customers). I want to display a message (to DBMS_OUTPUT) containing the number of rows that were inserted/updated/deleted.
I just want one message for each triggering statement, eg
'4 rows were inserted into customers table'.
How can I access the number of rows that are affected by the triggering statement from INSIDE the trigger declaration, ie XXX in the code below:
CREATE OR REPLACE TRIGGER customer_changes_trigger_2
AFTER INSERT OR UPDATE OR DELETE ON customers
DECLARE
v_operation VARCHAR(10);
v_number_rows NUMBER;
BEGIN
v_number := XXX;
IF INSERTING THEN
v_operation := 'inserted';
END IF;
IF UPDATING THEN
v_operation := 'updated';
END IF;
IF DELETING THEN
v_operation := 'deleted';
END IF;
DBMS_OUTPUT.PUT_LINE
(v_number_rows|| ' rows were ' || v_operation || ' from customers.');
END;
Can't find anything in the documentation, any help appreciated!
One way is to use a global variable to track the number of rows as there is no other way to get the row count from a statement level trigger. You would then need three triggers... one statement level to initialise the variable before the statement is run, one row level to add one to the variable for each row, one statement level to use the row count however you wish. First, set up the variable and a few procedures to help it:
create or replace package PKG_ROWCOUNT is
NUMROWS number;
procedure INIT_ROWCOUNT;
procedure ADD_ONE;
function GET_ROWCOUNT
return number;
end PKG_ROWCOUNT;
/
create or replace package body PKG_ROWCOUNT as
procedure INIT_ROWCOUNT is
begin
NUMROWS := 0;
end;
procedure ADD_ONE is
begin
NUMROWS := Nvl(NUMROWS, 0) + 1;
end;
function GET_ROWCOUNT
return number is
begin
return NUMROWS;
end;
end PKG_ROWCOUNT;
/
The first trigger to initialise the variable:
create or replace trigger CUSTOMER_CHANGES_TRIGGER_1
before insert or update or delete
on CUSTOMERS
begin
PKG_ROWCOUNT.INIT_ROWCOUNT;
end;
The second to update per row:
create or replace trigger CUSTOMER_CHANGES_TRIGGER_2
after insert or update or delete
on CUSTOMERS
for each row
begin
PKG_ROWCOUNT.ADD_ONE;
end;
/
The third to display the total:
create or replace trigger CUSTOMER_CHANGES_TRIGGER_3
after insert or update or delete
on CUSTOMERS
begin
Dbms_output.
PUT_LINE(PKG_ROWCOUNT.GET_ROWCOUNT || ' rows were affected.');
end;
I'm not 100$ sure if it's available inside AFTER trigger body, but you can try examining sql%rowcount