can't reference a custom function in a CHECK CONSTRAINT - oracle

i have a table with a column that has to be less than a certain value, the problem is this value is stored in another table so i cannot reference it so after some research i found out that i can use a function and call it in the check CONSTRAINT, here is my function
create or replace FUNCTION check_caution(id_cont in NUMBER)
return number
as res number ;
begin
select amount_contract*caution_offre_f*0.01 into res from contract
where id_contract=id_cont;
return res;
end check_caution;
the function works fine and i tested it.
and here is my CHECK CONSTRAINT
alter table caution_exe
add CONSTRAINT check_amount
check (caution_exe.amount_caution<=check_caution(caution_exe.id_contract));
but this is the message i get after i try to add it
ORA-00904: "CHECK_CAUTION": invalid identifier
00904. 00000 - "%s: invalid identifier"

As you can't reference your own function in check constraint, create a trigger which will do that job. Something like this:
CREATE OR REPLACE TRIGGER trg_biu_cauexe
BEFORE INSERT OR UPDATE
ON caution_exe
FOR EACH ROW
BEGIN
IF :new.amount_caution <= check_caution (:new.caution_exe.id_contract)
THEN
raise_application_error (-20000, 'Not allowed');
END IF;
END;

Related

How to write a function for updating audit column in oracle

I'm trying to write a trigger to update couple of columns in a table for that I wrote a function which is doing actual functionality and trigger to execute that function:
But I'm getting the exception can someone guide me what went wrong here:
Function I'm writing :
CREATE OR REPLACE FUNCTION myFunction()
RETURNS TRIGGER AS $$
BEGIN
IF OLD.timestamp IS NOT DISTINCT FROM NEW.timestamp THEN
NEW.lst_updt_ts = now();
END IF;
RETURN NEW;
END;
Trigger:
CREATE OR REPLACE TRIGGER Mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW
BEGIN
myFunction;
END;
When I'm compiling the function
below is the error I'm getting :
PLS-00103: Encountered the symbol ")" when expecting one of the following:
current delete exists prior
Compile error at line 1, column 34
The syntax is definitely not an Oracle. As far as I can understand, you need to update lst_updt_ts to current timestamp if user tries to update it manually. I'd try to write something like that
CREATE OR REPLACE TRIGGER Mytrigger
BEFORE UPDATE ON mytable
FOR EACH ROW
BEGIN
IF :OLD.timestamp <> :NEW.timestamp THEN
:NEW.lst_updt_ts = sysdate; -- or systimestamp;
END IF;
END;
The DBMS complains about you using an open parentheses here: myFunction( and then not having any parameters before closing with ). Remove the parentheses.
But then, Oracle doesn't support this trigger function syntax known from PostgreSQL. Neither does it support IS NOT DISTINCT FROM yet.
Unfortunately, you cannot even pass the row like this:
CREATE OR REPLACE FUNCTION myFunction(p_new IN OUT mytable.ROWTYPE%)
because Oracle doesn't treat :new and :old as row records. I've suggested this in Oracle Database Ideas last year. You can vote for it :-) Link: https://community.oracle.com/tech/apps-infra/discussion/comment/16785577#Comment_16785577
There are two ways that I see for now:
Put the code in your trigger directly.
Write a function getting :OLD.timestamp and :NEW.timestamp and :NEW.lst_updt_ts and returning the altered :NEW.lst_updt_ts.

Trigger with cursor shown error : Trigger is invalid and failed re-validation

I'm trying to create Oracle after insert trigger with the following code:
CREATE OR REPLACE TRIGGER AutoManhour
AFTER INSERT ON TBL_MSTPROJECT
FOR EACH ROW
DECLARE
CURSOR c_Section IS
SELECT IDSECTION AS IDSECTION FROM TBL_MSTSECTIONHR;
v_Section c_Section%ROWTYPE;
BEGIN
OPEN c_Section;
LOOP
FETCH c_Section INTO v_Section;
INSERT INTO TBL_TRXMANHOURS (ID_SECTION,INPUTBY_TRXMANHOURS,INPUTON_TRXMANHOURS,ID_PROJECT)
VALUES (v_Section.IDSECTION,'IT_ROBOT',SYSDATE,:new.IDPROJECT);
END LOOP;
CLOSE c_Section;
END AutoManhour;
/
But it retrieve an error :
ORA-04098: trigger 'DEPEEL.AUTOMANHOUR' is invalid and failed
re-validation
Why does this happen?
I think you must have used the incorrect column/table names or incorrect datatypes are used while inserting data into TBL_TRXMANHOURS. And of course, there is an infinite loop, You should make some condition to make it finite
Oracle will try to recompile invalid objects as they are referred to. Here the trigger is invalid
select * from user_errors where type = 'TRIGGER' and name = 'AUTOMANHOUR'
You can try the above query to find the actual error.
Cheers!!

How do I display a nested table in Oracle - PL/SQL?

I have an assignment where I'm supposed to create a user-defined function that will return all employees after 1968. My code is as follows:
First I create a new object, called emp_dobs, to hold the employees' firstname, lastname, and date of birth, using the same data types as the original employee table:
CREATE OR REPLACE TYPE emp_dobs AS OBJECT (
emp_fname VARCHAR2(20),
emp_lname VARCHAR2(20),
emp_dob DATE
);
/
Then I create emp_dobs_nested as a table of emp_dobs:
CREATE OR REPLACE TYPE emp_dobs_nested AS TABLE OF emp_dobs;
/
Lastly, I create a function that's supposed to return an emp_dobs_nested table:
CREATE OR REPLACE FUNCTION get_emp_dobs RETURN emp_dobs_nested
AS
dobs emp_dobs_nested;
BEGIN
SELECT emp_dobs(firstname, lastname, birthdate) BULK COLLECT INTO dobs
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
RETURN dobs;
END;
/
There is a weird quirk with compiling emp_dob_nested, however, where Oracle SQL Developer will display a new tab labeled "Output Variables - Log," and only show EMP_FNAME and EMP_LNAME. Despite that, everything compiles.
Now I want to test the function and display its results to prove that it works, but when I try this:
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
DBMS_OUTPUT.PUT_LINE(dobs);
END;
/
I get this error:
Error report -
ORA-06550: line 5, column 5:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
ORA-06550: line 5, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
What am I missing here? Thanks.
You cannot pass the whole collection to DBMS_OUTPUT, rather you must loop through it and display individual columns at each index.
DECLARE
dobs emp_dobs_nested;
BEGIN
dobs := get_emp_dobs;
FOR i IN 1..dobs.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
END LOOP;
END;
/
You can also use TABLE function to unnest a collection of objects into a relational resultset:
select * from table( get_emp_dobs )
Live demo: http://sqlfiddle.com/#!4/8cfb2/1
Another way of display is to use XML, try
DBMS_OUTPUT.PUT_LINE(XMLTYPE(dobs));
why not a create a view instead?
CREATE VIEW emp_view
AS
SELECT firstname, lastname, birthdate
FROM employee
WHERE birthdate < TO_DATE('01-JAN-1968', 'DD-MON-YYYY');
You can also try this,
DECLARE
dobs emp_dobs_nested;
i NUMBER := 0;
BEGIN
dobs := get_emp_dobs;
LOOP
i := dobs.NEXT(i);
DBMS_OUTPUT.PUT_LINE(dobs(i).emp_fname||','||dobs(i).emp_lname||','||dobs(i).emp_dob);
IF i = dobs.LAST THEN
EXIT;
END IF;
END LOOP;
END;
/

SQL Error: ORA-04091 while executing Trigger

I am trying to execute my trigger:
CREATE OR REPLACE TRIGGER Trg_video_bfr_delete
AFTER DELETE ON CMS_VIDEO
FOR EACH ROW
DECLARE
CODE varchar2(60);
BEGIN
SELECT CODE INTO CODE
from CMS_VIDEO
WHERE CODE = :OLD.CODE;
IF CODE IS NOT NULL THEN
INSERT INTO ASSET_DELETE_INDEX(CODE,ASSET_TYPE,IW_VPATH,LANGUAGE,MODIFIED_DTE)
VALUES (CODE,'Video',:old.IW_VPATH,:old.CONTENT_LANGUAGE,sysdate);
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO ASSET_DELETE_INDEX (CODE,ASSET_TYPE,IW_VPATH,LANGUAGE,MODIFIED_DTE)
VALUES (CODE,'Video',null,:old.CONTENT_LANGUAGE,sysdate);
COMMIT;
END Trg_video_bfr_delete;
/
But I am getting the following error while executing a delete command on the table
Error report -
SQL Error: ORA-04091: table LSDS.CMS_VIDEO is mutating, trigger/function may not see it
ORA-06512: at "LSDS.TRG_VIDEO_BFR_DELETE", line 6
ORA-04088: error during execution of trigger 'LSDS.TRG_VIDEO_BFR_DELETE'
04091. 00000 - "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.
Could anyone please help?
You have written trigger AFTER DELETE ON CMS_VIDEO. This trigger will fire when DELETE is performed from CMS_VIDEO table. So when CMS_VIDEO table is being modified, you cannot modify or query the same table in any of your triggers, procedures or functions.
CREATE OR REPLACE TRIGGER Trg_video_bfr_delete
AFTER DELETE ON CMS_VIDEO
FOR EACH ROW
BEGIN
IF :old.CODE IS NOT NULL THEN
INSERT INTO ASSET_DELETE_INDEX(CODE, ASSET_TYPE, IW_VPATH, LANGUAGE, MODIFIED_DTE)
VALUES (:old.CODE, 'Video', :old.IW_VPATH, :old.CONTENT_LANGUAGE, sysdate);
END IF;
END Trg_video_bfr_delete;
/
This is because you are deleting record from table and same table is used as reference inside trigger. This is case of "Mutating Error". You should switch to statement level trigger is it fits your requirement or you can use compound trigger which has multiple entry point and are capable of handling such scenarios.
Please refer https://oracle-base.com/articles/9i/mutating-table-exceptions
As per the snippet provided, I dont see any point in doing a SELECT on mutating TABLE.
We can easily ise :OLD.CODE to fetch the value. Hope below snippet helps.
CREATE OR REPLACE TRIGGER Trg_video_bfr_delete AFTER
DELETE ON CMS_VIDEO FOR EACH ROW
-- DECLARE CODE VARCHAR2(60);
BEGIN
-- SELECT CODE INTO CODE FROM CMS_VIDEO WHERE CODE = :OLD.CODE;
IF CODE IS NOT NULL THEN
INSERT
INTO ASSET_DELETE_INDEX
(
CODE,
ASSET_TYPE,
IW_VPATH,
LANGUAGE,
MODIFIED_DTE
)
VALUES
(
:old.code,
'Video',
:old.IW_VPATH,
:old.CONTENT_LANGUAGE,
sysdate
);
END IF;
EXCEPTION
WHEN OTHERS THEN
INSERT
INTO ASSET_DELETE_INDEX
(
CODE,
ASSET_TYPE,
IW_VPATH,
LANGUAGE,
MODIFIED_DTE
)
VALUES
(
:old.code,
'Video',
NULL,
:old.CONTENT_LANGUAGE,
sysdate
);
COMMIT;
END Trg_video_bfr_delete;

if insert error in oracle trigger creation?

CREATE TRIGGER Background_Process_Report_trit
AFTER INSERT
ON Background_Process_Report
FOR EACH ROW
IF INSERT(PROCESS_NAME)
BEGIN
SET EXECUTION_TIMESTAMP := NEW.TIMESTAMP;
END;
/
process_name -- column in my Background_Process_Report table.but i want to update the each time the process_name is created(by java application), trigger update the time in the EXECUTION_TIMESTAMP table.
but it is throwing the compliation error..
error:
IF INSERT(PROCESS_NAME)
*
ERROR at line 5:
ORA-04079: invalid trigger specification
how to reslove this error
If EXECUTION_TIMESTAMP is a table as you say, then it must have a column you want to update, let's call it TIMESTAMP_COL. The the trigger would be something like:
CREATE TRIGGER Background_Process_Report_trit
AFTER INSERT
ON Background_Process_Report
FOR EACH ROW
WHEN (NEW.PROCESS_NAME IS NOT NULL)
BEGIN
UPDATE EXECUTION_TIMESTAMP
SET TIMESTAMP_COL = NEW.TIMESTAMP
WHERE ???; -- Change ??? to the appropriate condition
END;
/
I have assumed that by "IF INSERT(PROCESS_NAME)" you mean "if a non-null value is inserted into PROCESS_NAME" and created a WHEN clause to match.

Resources