enter image description here
I have this user table.
and i created a CRUD for it to get add and update users.
now i wanted to create a stored procedure to approve or reject users.
which will give
0= checker , 1= maker
0= pending , 1= approved, 2= reject
create or replace procedure check_user (p_user_id IN varchar2,
p_user_type OUT varchar2,
p_user_status OUT varchar2) is
cursor c_user is select user_type, user_status
from your_user_table
where user_id = p_user_id;
begin
open c_user;
fetch c_user into p_user_type, p_user_status;
if c_user%notfound then
p_user_type := 'unknown';
p_user_status := 'unknown';
end if;
close c_user;
end;
/
Related
Well I'm trying to call a stored procedure in my trigger.
The error I'm getting
"table %s.%s is mutating, trigger/function may not see it"
*Cause: A trigger (or a user defined plsql function that is referenced in
this statement) attempted to look at (or modify) a table that was
in the middle of being modified by the statement which fired it.
*Action: Rewrite the trigger (or function) so it does not read that table.
This is my code of the trigger:
create or replace TRIGGER check_salary_trg
AFTER INSERT OR UPDATE ON employees
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE(:new.job_id ||' '|| :new.salary);
check_salary(:new.job_id, :new.salary);
END;
This is my stored procedure:
PROCEDURE check_salary (job_id employees.job_id%TYPE, salary employees.salary%TYPE)
IS
maxSal NUMBER;
minSal NUMBER;
BEGIN
SELECT MAX(salary) INTO maxSal FROM employees WHERE job_id = job_id;
SELECT MIN(salary) INTO minSal FROM employees WHERE job_id = job_id;
IF maxSal >= salary OR minSal <= salary THEN
RAISE_APPLICATION_ERROR(-20001,'Invalid slary '||salary||'. Salaries for job '||job_id||' must be between '||minSal||' and '||maxSal);
ELSE
DBMS_OUTPUT.PUT_LINE('Test');
END IF;
END;
This is how I try to see that the trigger is working:
UPDATE employees SET salary = 100000 WHERE employee_id = 100;
Somehow the DBMS_OUTPUT.PUT_LINE in my Trigger code is working. But the stored procedure causes the error.
You can't do SELECT or any other DML(INSERT,UPDATE,DELETE) on the table on which trigger is being fired.You have to use compound trigger to get away with mutating table error.
The procedure invoked by trigger is doing a SELECT on the employee table on which trigger is firing and that is forbidden by oracle.
A working example for same issue can be found under, refer UPDATE section Trigger selecting child records, multiplying their values and updating parent record
first of all, why do you query the same table twice in your procedures? that's a huge waste of resources... rather run
SELECT min(salary), max(salary) INTO minSal, maxSal
FROM employees
WHERE job_id = job_id
Second of all, you cannot query the same table you are update.ing !! That's a huge data (in)consistency issue. Why don't you run this in a package/procedure instead? That will give you way better control over your flow
Something like:
CREATE OR REPLACE PROCEDURE prcd_update_salary(p_emp_id INT, p_salary INT)
IS
maxSal INT;
minSal INT;
job_id INT;
BEGIN
SELECT job_id, min(salary), max(salary) INTO job_id, minSal, maxSal
FROM employees
WHERE job_id = (SELECT job_id FROM employees WHERE employee_id = p_emp_id);
IF (p_salary >= minSal AND p_salary <= maxSal) THEN
UPDATE employees SET salary = p_salary WHERE employee_id = p_emp_id;
ELSE
dbms_output.put_line('Sorry, this is out of range!')
dmbs_output.put_line('You can only use from '||minSal||' up to '||maxSal||' for a job id: '|| job_id);
END IF;
END;
This is, of course, only a sample code to give you hints on how to do that.. you have all the logic in one place and not in two diff objects (very hard to debug !!) ... in your production code you have to sanitize the input a maybe do a bit more checks and of course proper indexing is a must - but this pretty much summarizes what I would do :)
I want to use 1st parameter of my procedure EMP_ID as IN OUT parameter. Originally it's IN parameter and this procedure is working fine, but as the last line concern
htp.p('Inserted for Employee-id '||EMP_ID);
I want to use this line in anonymous block and most importantly it should be a bind variable because I am creating the REST API in which user will only enter values and it will be taken as bind variable in oracle Apex and the below procedure is working fine with respect of IN parameter.
create or replace procedure att_time_ins (EMP_ID in varchar2, ORG_ID in number,V_TIME_STATUS in number) is
BEGIN
INSERT INTO TIME_ATTENDANCE_POOL
(EMPLOYEE_ID, ATTENDANCE_DATE,TIME_HOURS,TIME_MINUTES,TIME_STATUS,LOCATION_ID,ORG_ID,PREPARED_ON )
VALUES
(EMP_ID, to_date(sysdate,'DD/MM/YYYY'),to_char(sysdate,'HH24') ,to_char(sysdate,'MI'),V_TIME_STATUS,null,ORG_ID,
to_date(sysdate,'DD/MM/YYYY') );
COMMIT;
time_management.create_attendance_sheet(v_org_id => ORG_ID,
v_employee_id => EMP_ID,
target_date => to_date(sysdate,'DD/MM/YYYY'));
htp.p('Inserted for Employee-id '||EMP_ID);
end att_time_ins;
I am calling my procedure in this way
begin
att_time_ins(:employee_id,:org_id,:time_status);
end;
Please help me to modify this stuff according to IN OUT Parameter i.e Employee_id should be IN OUT parameter. There is no proper documentation regarding passing bind variables as in out prameter in PLSQL Block.
Let us say you have a procedure named PR_PROC, you can use VARIABLE statement for passing IN OUT or OUT kind of variables.
CREATE OR REPLACE PROCEDURE PR_PROC (EMP_NAME IN VARCHAR2,
EMP_ID IN OUT VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE (EMP_NAME||EMP_ID);
END;
VARIABLE KURSOR VARCHAR2
BEGIN
:KURSOR:='4';
PR_PROC('SENIOR',:KURSOR);
END;
Note: If you are using TOAD Editor you can press F5 to make it work.
Oracle VARIABLE
i ve got some database with customer id, names, surnames. I made procedure with some parameters, which includes cursor with parameters. And i want with that cursor show customer names, surnames, id. And i want show that, where i call procedure, and i for example write as parameters in procedure for example NULL NULL, it show me all record. Next if i write NULL, Surname, it show me records with that surnames. If i write firstname,NULL, it show record only records with that first name and if i write Firstname,lastname, it show me only that record which matching this. So my procedure works fine, but i need right where clause n cursor. This is my cursor
CREATE OR REPLACE PROCEDURE test_two(c_f_name VARCHAR2,c_l_name VARCHAR2) IS
CURSOR c2(f_name VARCHAR2,l_name VARCHAR2) IS
SELECT cus_id,cus_l_name,cus_f_name
FROM CUSTOMER
WHERE (cus_f_name IS NOT NULL AND (cus_f_name = f_name orcus_l_name
IS NULL))
AND (cus_l_name IS NOT NULL AND (cus_l_name = l_name or cus_f_name IS
NULL));
v_complex c2%ROWTYPE;
lv_show VARCHAR2(20);
f_name VARCHAR2(20) := c_f_name;
l_name VARCHAR2(20) := c_l_name;
BEGIN
open c2(f_name,l_name);
LOOP
FETCH c2 INTO v_complex;
dbms_output.put_line(v_complex.cus_id|| ' ' ||v_complex.cus_f_name|| ' '
||v_complex.cus_l_name);
EXIT WHEN c2%NOTFOUND;
END LOOP;
CLOSE c2;
END;
EVERYTHING IS IFNE IN THAT PROCEDURE BUT I NEED ONLY RIGHT WHERE CLAUSE! THANK YOU
You could use a combination of LIKE and NVL.
WHERE cus_f_name LIKE NVL(c_f_name, '%')
AND cus_l_name LIKE NVL(c_l_name, '%');
Explanation:
The function NVL returns the first parameter if it is not NULL.
Otherwise it returns the second parameter.
Trying to create a trigger that prevents duplicate acct# and null acct balance. Trigger is able to create but when inserting an duplicate acct# to test the trigger, got error mesg that "trigger is invalid and failed re-validation".
create or replace trigger update_acct#
before insert or update on ACCOUNT
for each row
declare
v_cta# NUMBER;
begin
select count(:new.A#) into v_cta# from account group by A#;
if (v_cta#>1 or :new.bal=null) then
raise_application_error (-20011, 'DUPLICATE ACCOUNT NUMBER OR BAL CANNOT BE NULL');
end if;
end;
You absolutely positively do not want to use a trigger for this. Use a unique constraint to prevent duplicate account numbers.
ALTER TABLE account ADD CONSTRAINT a#_unique UNIQUE (a#);
Make your balance field not accept nulls.
ALTER TABLE account MODIFY bal NOT NULL;
For purely educational purposes here is how you'd implement this with your trigger. Yours was close, except you want to use :new.a# to count rows and not have it expressly in the COUNT() and that you want an AFTER trigger since you want to see if your newly added account is the first duplicate.
create or replace trigger update_acct#
after insert or update on ACCOUNT
for each row
DECLARE
v_cta# INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_cta#
FROM account
WHERE a# = :new.a#;
IF v_cta# > 1 OR :new.bal = NULL THEN
raise_application_error(-20011,
'DUPLICATE ACCOUNT NUMBER OR BAL CANNOT BE NULL');
END IF;
END update_acct#;
/
Lets say I have a table as follows--
create table employees
(
eno number(4) not null primary key,
ename varchar2(30),
zip number(5) references zipcodes,
hdate date
);
And I'm trying to create a trigger with--
CREATE OR REPLACE TRIGGER TWELVE_ONE
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
V_DATE VARCHAR2 (10);
BEGIN
SELECT TO_CHAR (SYSDATE, 'hh24:mi:ss') INTO V_DATE FROM DUAL;
IF (V_DATE >= '12:00:01' AND V_DATE < '13:00:00')
THEN
INSERT INTO TABLE ?????
ELSE
ROLLBACK? TERMINATE TRANSACTION?
END IF;
END;
Purpose of the trigger is to allow an insertion/update during 12:00-13:00 and prevent the insertion at any other time. The trigger construction (thanks to #Melkikun) is seems ok. However now I'm facing the following issues--
How is it possible to pass the values here? I mean lets say my create statement is:
Insert into employees Values (1, 'someone', 11111, '17-12-2015')
And lets say the time is 12:30:01 now. How would the trigger perform the insertion without knowing the values?
And lets say the time is now 13:00:1 now. How would the trigger stop/prevent the insertion?
I'm using Oracle SQL Developer 4.02.15
Many Thanks
You just have to do it the other way.
If the time is not correct,then you raise an exception, so the insert won't be done.
CREATE OR REPLACE TRIGGER TWELVE_ONE
BEFORE INSERT OR UPDATE
ON EMPLOYEES
FOR EACH ROW
DECLARE
V_DATE VARCHAR2 (10);
MyException exception;
BEGIN
SELECT TO_CHAR (SYSDATE, 'hh24:mi:ss') INTO V_DATE FROM DUAL;
IF (V_DATE < '12:00:01' OR V_DATE > '13:00:00')
THEN
raise MyException;
END IF;
EXCEPTION
When MyException then
ROLLBACK;
//output message ...
END;
How would the trigger perform the insertion without knowing the values?
The trigger knows the value thanks to :NEW and :OLD.
You normally use the terms in a trigger using :old to reference the old value and :new to reference the new value.So you will have :NEW.eno ,:NEW.ename ...
Here is an example from the Oracle documentation :
CREATE OR REPLACE TRIGGER Print_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON Emp_tab
FOR EACH ROW
WHEN (new.Empno > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :new.sal - :old.sal;
dbms_output.put('Old salary: ' || :old.sal);
dbms_output.put(' New salary: ' || :new.sal);
dbms_output.put_line(' Difference ' || sal_diff);
END;