I need to develop a trigger in PL/SQL (Oracle) before INSERT.
In this table there is a column (cdat) of type DATE.
Let's say i do INSERT INTO myTbl (123,'12/05/2011');
In my trigger the :NEW.CDAT is converted in the final date system or it's still a varchar?
Do I need to do a TO_DATE(:NEW.CDAT) to get the date value?
:NEW.CDAT will be a date. The :new and :old variables in triggers are always the type of the destination field.
I wasn't able to find anything in the Oracle documentation that confirms my statement, but I was able to devise some experimental proof:
CREATE TABLE test2 (a DATE);
CREATE OR REPLACE TRIGGER bu_test2
BEFORE INSERT OR UPDATE
ON test2
FOR EACH ROW
DECLARE
PROCEDURE type_test(in_type DATE) IS
BEGIN
DBMS_OUTPUT.put_line('date');
END;
PROCEDURE type_test(in_type VARCHAR2) IS
BEGIN
DBMS_OUTPUT.put_line('varchar2');
END;
BEGIN
type_test(:new.a);
END;
INSERT INTO test2
VALUES ('24-Mar-2011');
Since type_test is overloaded, Oracle will choose which procedure to use based on the type being passed in. The results of this script are:
Table created.
Trigger created.
date
1 row created.
You need to do a conversion if your session parameter 'NLS_DATE_FORMAT' is not 'mm/dd/yyyy'.
For example:
create table myTbl (id number, cdat date);
select *
from nls_session_parameters ns
where ns.parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------------------------------------------
NLS_DATE_FORMAT DD-MON-RR
In this case, without a to_Date you'll get an error:
insert into myTbl
values
(123, '12/05/2011');
ORA-01843: not a valid month
You can change this paramter at session level, system level, etc.
zep#dev> alter session set NLS_DATE_FORMAT = 'mm/dd/yyyy';
Session altered
select *
from nls_session_parameters ns
where ns.parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------------------------------------------------------------------------
NLS_DATE_FORMAT mm/dd/yyyy
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
1 row inserted
zep#dev> select *
2 from myTbl;
ID CDAT
---------- -----------
123 05/12/2011
Test on a row level trigger
truncate table Mytbl;
alter session set NLS_DATE_FORMAT = 'DD-MON-RR';
create or replace trigger befins_myTbl
before insert on myTbl
for each row
declare
begin
-- demo
:new.cdat := :new.cdat + numtoyminterval(1,'YEAR');-- (demo trigger add 1 year )
end;
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
Output: ORA-01843: not a valid month
alter session set NLS_DATE_FORMAT = 'mm/dd/yyyy';
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
commit;
select *
from myTbl;
Output
ID CDAT
---------- -----------
123 12/05/2012
Related
I have a table temp_table with the following columns
Id number,
name varchar,
Password varchar,
pwd_change_date timestamp
I want to capture the timestamp in pwd_change_date column only when password column is changed.
So basically i want to use update statement inside the trigger to update timestamp value in pwd_change_date column for the same record.
Example
When a password is changed for one user, I want to capture the timestamp value in pwd_change_date for the same record.
I tried with before insert and after insert of password on temp_table, but getting mutation error. Is it allowed in Oracle to update the Same row/table on which trigger is fired?
You don't need to update the table again; you can modify the data before it is inserted, with a before-insert row level trigger, e.g.:
create trigger trig_pwd_date
before insert or update on temp_table
for each row
when (old.password is null and new.password is not null or new.password != old.password)
begin
:new.pwd_change_date := systimestamp;
end;
/
db<>fiddle demo
This used the new and old correlation names to decide if the password value has changed; and the new correlation name to assign the system time to the field in the pseudorecord, which becomes the column value when the insert completes.
Hopefully you aren't storing plain-text passwords in your table.
SQL> create table temp_table (password varchar2(50), pwd_change_date TIMESTAMP);
Table created.
SQL> create trigger trig_pwd_date
before insert or update on temp_table
for each row
when (old.password is null and new.password is not null or new.password != old.password)
begin
:new.pwd_change_date := systimestamp;
end; 2 3 4 5 6 7
8 /
Trigger created.
SQL> set time on
15:28:42 SQL> insert into temp_table values ('23456',sysdate);
1 row created.
15:29:01 SQL> commit;
Commit complete.
15:29:09 SQL> select * from temp_table;
PASSWORD
PWD_CHANGE_DATE
12345
21-SEP-20 03.28.02.370377 PM
23456
21-SEP-20 03.29.01.478017 PM
I'm trying to create a trigger to get value of validity from plan table and add it to the value of startdate to get the enddate into dblog table.I have written this trigger.
My dblog and plan table schema is like this
desc dblog;
Name Null? Type
---------------------------- -------- -------
PLANID NOT NULL NUMBER
STARTDATE DATE
ENDDATE NOT NULL DATE
desc plan;
Name Null? Type
---------------------------- -------- -------
PLANID NOT NULL NUMBER
VALIDITY NOT NULL NUMBER
By default column STARTDATE has SYSDATE value.
CREATE OR REPLACE TRIGGER trg2
BEFORE INSERT ON dblog FOR EACH ROW
DECLARE
l_startdate date := SYSDATE;
l_enddate date;
l_validity number;
BEGIN
SELECT validity INTO l_validity
FROM plan
WHERE planid = :NEW.planid;
l_endate := l_startdate + l_validity;
SET :NEW.enddate := l_enddate;
END;
/
it shows following error:
10/2 PL/SQL: SQL Statement ignored
10/6 PL/SQL: ORA-00922: missing or invalid option
Am I using wrong concept or what? How can i get this done?
l_startdate SYSDATE;
You have not declared the data type of the variable, SYSDATE is an inbuilt function and not a data type.
If you want to assign the value at the time of data type declaration, then do it as:
DECLARE
l_startdate DATE := SYSDATE;
For example,
SQL> set serveroutput on
SQL> DECLARE
2 start_dt DATE := SYSDATE;
3 BEGIN
4 dbms_output.put_line(start_dt);
5 END;
6 /
29-SEP-15
PL/SQL procedure successfully completed.
SQL>
UPDATE OP edited the question
The below query will fail:
SELECT validity INTO l_validity from plan where planid=:new.planid;
since your trigger is defined on dblog table, however, you are referring to the :new values for plan table.
Also,
SET :NEW.enddate := l_enddate;
You don't need the SET clause, simply do it as:
:NEW.enddate := l_enddate;
I have a table called TBL_CAS. In that, FLD_ID as auto increment column and another column is called FLD_CAS_CODE. Now I need to add CAS- as a prefix to FLD_ID and Insert into FLD_CAS_CODE. I need to do this in trigger. I was tried with the below code, But the data in not inserting, What is the problem ?
CREATE OR REPLACE TRIGGER TBL_CAS_TRG
BEFORE INSERT ON TBL_CAS
FOR EACH ROW
BEGIN
:NEW.FLD_CAS_CODE := TO_CHAR ('CAS')||'-'||:NEW.FLD_ID;
END;
I mean `"cas-"+"fld_id"="cas-fld_id"'
You don't need to put TO_CHAR() around things which are already charcater datatypes. But you should cast the numeric identifier (rather than relying on implicit conversion):
:NEW.FLD_CAS_CODE := 'CAS-'||TRIM(TO_CHAR (:NEW.FLD_ID));
which part isn't working exactly? as your trigger seem to work just fine.
SQL> create table TBL_CAS( FLD_ID number, FLD_CAS_CODE varchar2(20));
Table created.
SQL> CREATE OR REPLACE TRIGGER TBL_CAS_TRG
2 BEFORE INSERT ON TBL_CAS
3 FOR EACH ROW
4 BEGIN
5 :NEW.FLD_CAS_CODE := TO_CHAR ('CAS')||'-'||:NEW.FLD_ID;
6 END;
7 /
Trigger created.
SQL> insert into TBL_CAS (fld_id) values (1001);
1 row created.
SQL> select * From TBL_CAS;
FLD_ID FLD_CAS_CODE
---------- --------------------
1001 CAS-1001
SQL>
This will also work fine:
CREATE OR REPLACE TRIGGER TBL_AREA_CODES_TRG
BEFORE INSERT ON TBL_AREA_CODES
FOR EACH ROW
BEGIN
:NEW.OBRM_AREA_CODE := :NEW.STATE_CODE ||'-'||:NEW.DIST_CODE ||'-'||:NEW.CITY_CODE ||'-'||:NEW.AREA_CODE ;
END;
When i'm using FML999G999G999G999G990D00 as my Number Format Mask in Oracle Apex, it shows Value as $800.00. I need to Replace $ with another Currency Sign.
How Could i do this ?
You need to modify the session parameter NLS_CURRENCY to change the currency:
SQL> ALTER SESSION SET NLS_CURRENCY='EUR';
Session altered.
SQL> SELECT to_char(800, 'FML999G999G999G999G990D00') FROM dual;
TO_CHAR(800,'FML999G999G999G999G9
---------------------------------
EUR800,00
Or with DBMS_SESSION:
SQL> BEGIN dbms_session.set_nls('NLS_CURRENCY', 'GBP'); END;
2 /
PL/SQL procedure successfully completed.
SQL> SELECT to_char(800, 'FML999G999G999G999G990D00') FROM dual;
TO_CHAR(800,'FML999G999G999G999G9
---------------------------------
GBP800,00
You can also specify the currency directly with TO_CHAR:
SQL> SELECT to_char(800, 'FML999G990D00', 'NLS_CURRENCY=''£''') FROM dual;
TO_CHAR(800,'FML999G9
---------------------
£800,00
I'm inserting data in table through this statement:
insert into CATEGORY_MASTER (
CAT_MAS_ID,
DESCRIPTION, ORG_ID, STATUS, MODIFY_EMPID, LANGUAGE_ID, LG_IP_MAC)
values (
( SELECT COALESCE(MAX(ct.cat_mas_id), 0)+1
FROM category_master ct),
'fff', 2, 'A', 52,1,'SYSTEM/127.0.0.1/NOTDEFINE')
The target table has this trigger:
create or replace trigger trg_aft_i_u_category_master
after insert OR UPDATE of cat_mas_id,status on category_master FOR EACH ROW
DECLARE
CURSOR CSTYPE IS
SELECT CST.SUB_TYPE_ID,CST.TYPE_ID,CST.ORG_ID,CST.STATUS
FROM COMPLAINT_SUB_TYPE CST
WHERE CST.ORG_ID=:NEW.ORG_ID AND CST.STATUS='A';
V_CSTYPE CSTYPE%ROWTYPE;
BEGIN
IF CSTYPE%ISOPEN THEN
CLOSE CSTYPE;
END IF;
OPEN CSTYPE;
LOOP
FETCH CSTYPE INTO V_CSTYPE;
EXIT WHEN CSTYPE%NOTFOUND;
if INSERTING then
/******** Suspect issue here *******/
INSERT INTO CATEGORY_DETAILS(
CAT_DTL_ID, CAT_MAS_ID, TYPE_ID ,SUB_TYPE_ID,
ORG_ID,MAP_STATUS,MODIFY_EMPID,LANGUAGE_ID,LG_IP_MAC)
VALUES (SEQ_CATEGORY_DETAILS.NEXTVAL,:NEW.CAT_MAS_ID,
V_CSTYPE.TYPE_ID,V_CSTYPE.SUB_TYPE_ID,:NEW.ORG_ID,'U',
:NEW.MODIFY_EMPID,:NEW.LANGUAGE_ID,:NEW.LG_IP_MAC);
/************************************/
end if;
if UPDATING then
if :new.status = 'I' then
UPDATE CATEGORY_DETAILS CD
SET CD.MAP_STATUS= 'U'
WHERE CD.CAT_MAS_ID=:NEW.CAT_MAS_ID AND CD.ORG_ID=:NEW.ORG_ID;
end if;
end if;
END LOOP;
CLOSE CSTYPE;
end trg_aft_i_u_category_master;
The explanantion for ORA-01438 is:
"value larger than specified precision allowed for this column"
So one of your tables (not necessarily MASTER_CATEGORY) has a number column defined with significant digits, and your code is trying to insert a number which is too large.
Given this table ...
SQL> create table t42 (col1 number(5,2));
Table created.
SQL>
... we can insert a value which fits the declaration:
SQL> insert into t42 values (123.45);
1 row created.
SQL>
... the database rounds up trailing decimals:
SQL> insert into t42 values (12.345);
1 row created.
SQL>
... and the database rejects the value when the leading element is too large:
SQL> insert into t42 values (1234.5);
insert into t42 values (1234.5)
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
SQL>
This now becomes an issue for you. You need to describe your tables to see which columns are defined as precise numbers, that is like NUMBER(3) or NUMBER(7,2). Then check the data you are using to estabish which numeric value is too big. Standard debugging techniques will help.