Fire trigger based on two entries in table - oracle

I would like to know if it is possible to check a table for two conditions in a trigger.
More precisely, I want to decide what to enter based on entries from two columns.
create or replace TRIGGER AUDIT_TABLE1
-- starts on every update or insert
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
DECLARE
v_user varchar2(30);
v_userid USERS.UUID%TYPE;
BEGIN
v_user := SYS_CONTEXT('APEX$SESSION','APP_USER');
select UUID into v_userid from USERS where lower(username)=lower(v_user);
IF :NEW.COLUMN4 ='REQUESTED' AND :NEW.COLUMN5='TEXT123' THEN
INSERT INTO AUDIT_TABLE1
(COLUMN1,COLUMN2,COLUMN3)
VALUES
(:NEW.COLUMN1, :NEW.COLUMN2, 'TEXT123');
ELSIF :NEW.COLUMN4 ='REQUESTED' AND :NEW.COLUMN5='TEXT3265' THEN
INSERT INTO AUDIT_TABLE1
(COLUMN1,COLUMN2,COLUMN3)
VALUES
(:NEW.COLUMN1, :NEW.COLUMN2, 'TEXT52354');
END IF;
END;
I'll get no error when compiling this trigger but I as well get no entry in the AUDIT_TABLE1 table.

The principle is OK, which means that you either didn't post what you really have (which might be the case, regarding column names), or you didn't insert what you should.
Sample tables:
SQL> CREATE TABLE table1
2 (
3 column4 VARCHAR2 (20),
4 column5 VARCHAR2 (20)
5 );
Table created.
SQL> CREATE TABLE audit_table
2 (
3 column4 VARCHAR2 (20),
4 column5 VARCHAR2 (20),
5 column6 VARCHAR2 (20)
6 );
Table created.
SQL>
Trigger:
SQL> CREATE OR REPLACE TRIGGER audit_table1
2 -- starts on every update or insert
3 AFTER INSERT OR UPDATE
4 ON table1
5 FOR EACH ROW
6 DECLARE
7 v_user VARCHAR2 (30);
8 BEGIN
9 v_user := SYS_CONTEXT ('APEX$SESSION', 'APP_USER');
10
11 IF :new.column4 = 'REQUESTED'
12 AND :new.column5 = 'TEXT123'
13 THEN
14 INSERT INTO audit_table (column4, column5, column6)
15 VALUES (:new.column4, :new.column5, 'TEXT123');
16 ELSIF :new.column4 = 'REQUESTED'
17 AND :new.column5 = 'TEXT3265'
18 THEN
19 INSERT INTO audit_table (column4, column5, column6)
20 VALUES (:new.column4, :new.column5, 'TEXT52354');
21 END IF;
22 END;
23 /
Trigger created.
Testing:
These values won't insert anything because none of IF conditions is met:
SQL> INSERT INTO table1
2 VALUES ('x', 'y');
1 row created.
SQL> SELECT * FROM audit_table;
no rows selected
SQL>
These values will insert a row into the audit table:
SQL> INSERT INTO table1
2 VALUES ('REQUESTED', 'TEXT123');
1 row created.
SQL> SELECT * FROM audit_table;
COLUMN4 COLUMN5 COLUMN6
-------------------- -------------------- --------------------
REQUESTED TEXT123 TEXT123
SQL>

Related

Stored procedure with select count(*) and use count in IF statement

I am creating a stored procedure in Oracle database that's resulting in error "ORA-01858: a non-numeric character was found where a numeric was expected".
My procedure is as below:
create or replace procedure testProc(
id IN VARCHAR2,
user IN VARCHAR2,
sender IN VARCHAR2
)
as
vCount number;
begin
select count(*) into vCount from table1 where id='12345'
if vCount=0
insert into table1 (id, user, sender, status) values (id, user, partner, status);
else
update table1 set status='ERR' where id='12345'
end if;
end procedure;
Error: ORA-01858: a non-numeric character was found where a numeric was expected
I tried replacing vCount as int that did not help. Also tried declaring vCount below sender IN VARCHAR2.
Can someone please tell what is correct way to use the above procedure.
Use a MERGE statement then you can do it in a single statement (rather than SELECT followed by either INSERT or UPDATE):
CREATE PROCEDURE testProc(
i_id IN table1.id%TYPE,
i_user IN table1."USER"%TYPE,
i_sender IN table1.sender%TYPE,
i_status IN table1.status%TYPE
)
AS
BEGIN
MERGE INTO table1 dst
USING (
SELECT '12345' AS id
FROM DUAL
) src
ON (src.id = dst.id)
WHEN MATCHED THEN
UPDATE SET status = 'Err'
WHEN NOT MATCHED THEN
INSERT (id, "USER", sender, status)
VALUES (i_id, i_user, i_sender, i_status);
END testProc;
/
db<>fiddle here
This code can't possibly return error you specified because
procedure is invalid (mising statement terminators; column name can't be USER because it is a keyword, reserved for currently logged user)
that error code is related to date issues, while - in your code - there's nothing that looks like a date
Therefore, it is impossible to help you with error you stated. Otherwise, consider NOT naming procedure's parameters the same as column names because that leads to various problems.
Something like this would work, but it is not related to error you got.
Sample table:
SQL> CREATE TABLE table1
2 (
3 id VARCHAR2 (5),
4 c_user VARCHAR2 (20),
5 partner VARCHAR2 (10),
6 sender VARCHAR2 (10),
7 status VARCHAR2 (5)
8 );
Table created.
SQL>
Procedure:
SQL> CREATE OR REPLACE PROCEDURE testProc (p_id IN VARCHAR2,
2 p_user IN VARCHAR2,
3 p_sender IN VARCHAR2)
4 AS
5 vCount NUMBER;
6 BEGIN
7 SELECT COUNT (*)
8 INTO vCount
9 FROM table1
10 WHERE id = p_id;
11
12 IF vCount = 0
13 THEN
14 INSERT INTO table1 (id,
15 c_user,
16 sender,
17 status)
18 VALUES (p_id,
19 p_user,
20 NULL,
21 'NEW');
22 ELSE
23 UPDATE table1
24 SET status = 'ERR'
25 WHERE id = p_id;
26 END IF;
27 END testproc;
28 /
Procedure created.
Testing:
SQL> EXEC testproc('12345', 'Little', 'Foot');
PL/SQL procedure successfully completed.
SQL> SELECT * FROM table1;
ID C_USER PARTNER SENDER STATU
----- -------------------- ---------- ---------- -----
12345 Little NEW
SQL> EXEC testproc('12345', 'Little', 'Foot');
PL/SQL procedure successfully completed.
SQL> SELECT * FROM table1;
ID C_USER PARTNER SENDER STATU
----- -------------------- ---------- ---------- -----
12345 Little ERR
SQL>

In Oracle SQL, I would like to call a Oracle stored procedure and then select the value of an OUT parameter as a column result. Is this possible?

CREATE OR REPLACE PROCEDURE myStoredProcedure (idParam IN VARCHAR2,
outputParam OUT VARCHAR2)
AS
BEGIN
SELECT OUTPUTCOL INTO outputParam FROM MyTable WHERE ID = idParam;
END;
DECLARE
v_OutputResults VARCHAR2(20);
BEGIN
myStoredProcedure('123', v_OutputResults);
SELECT v_OutputResults AS ColumnResult FROM DUAL;
END;
If we understand your goal, you should be using a function, not a procedure:
First, we set up the example:
SQL> -- conn system/halftrack#pdb01
SQL> conn scott/tiger#pdb01
Connected.
SQL> --
SQL> CREATE TABLE my_table (
2 user_id number not null,
3 Name varchar2(10)
4 )
5 ;
Table created.
SQL> --
SQL> insert into my_table values (1,'Bob');
1 row created.
SQL> insert into my_table values (2,'Carol');
1 row created.
SQL> insert into my_table values (3,'Ted');
1 row created.
SQL> insert into my_table values (4,'Alice');
1 row created.
SQL> commit;
Commit complete.
SQL> select * from my_table;
USER_ID NAME
---------- ----------
1 Bob
2 Carol
3 Ted
4 Alice
4 rows selected.
Now we create the function, then use it:
SQL> --
SQL> create or replace function my_function (id_param number)
2 return varchar2
3 is
4 v_name varchar2(10);
5 begin
6 select name
7 into v_name
8 from my_table
9 where user_id = id_param
10 ;
11 --
12 return v_name;
13 end;
14 /
Function created.
SQL> show errors
No errors.
SQL> --
SQL> select my_function(1) from dual;
MY_FUNCTION(1)
--------------------------------------------------------------------------------
Bob
1 row selected.
And clean up our example:
SQL> --
SQL> drop table my_table purge;
Table dropped.
No but you can do so using a stored function.

PL/SQL: How to insert all records from table type to another table without for loop

I have probably trivial problem but I can't nail the logic quite right.
I have following types:
create or replace TYPE test_rec FORCE
AS OBJECT (ref_id NUMBER (20)
,ref_type VARCHAR2 (4));
create or replace TYPE test_ref_tbl FORCE
AS TABLE OF test_rec;
and actual table
CREATE TABLE my_tbl
( id number(10) NOT NULL,
ref_id varchar2(20) NOT NULL,
ref_type varchar2(4),
CONSTRAINT my_pk PRIMARY KEY (id)
);
Now, in one procedure I get variable test_ref_tbl with data and I have to insert everything to my_tbl, also id should be generated from sequence.
I managed to do this quite easily with for loop
FOR i IN 1 .. test_ref_tbl.COUNT LOOP
INSERT INTO my_tbl(id
,ref_id
,ref_type)
VALUES (my_test_sequence.NEXTVAL
,test_ref_tbl(i).ref_id
,test_ref_tbl(i).ref_type
);
END LOOP;
and everything works fine, but I got alot of flack for inserting data in for loop, I'm not plsql developer so maybe my colleagues are making my job harder just for the hell of it.
But to get back on topic, is there a way to do this without for loop?
Thanks
Yes, there is. Here's an example:
Creating test case first:
SQL> CREATE OR REPLACE TYPE test_rec FORCE AS OBJECT
2 (
3 ref_id NUMBER (20),
4 ref_type VARCHAR2 (4)
5 );
6 /
Type created.
SQL> CREATE OR REPLACE TYPE test_ref_tbl FORCE AS TABLE OF test_rec;
2 /
Type created.
SQL> CREATE TABLE my_tbl
2 (
3 id NUMBER (10) NOT NULL,
4 ref_id VARCHAR2 (20) NOT NULL,
5 ref_type VARCHAR2 (4),
6 CONSTRAINT my_pk PRIMARY KEY (id)
7 );
Table created.
SQL> CREATE SEQUENCE my_test_sequence;
Sequence created.
As data source, I'm using Scott's DEPT table.
SQL> DECLARE
2 l_tab test_ref_tbl;
3 BEGIN
4 SELECT test_rec (deptno, SUBSTR (dname, 1, 4))
5 BULK COLLECT INTO l_tab
6 FROM dept;
7
8 -- this is what you're looking for
9 INSERT INTO my_tbl (id, ref_id, ref_type)
10 SELECT my_test_sequence.NEXTVAL, t.*
11 FROM TABLE (l_tab) t;
12 END;
13 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM my_tbl;
ID REF_ID REF_
---------- -------------------- ----
1 10 ACCO
2 20 RESE
3 30 SALE
4 40 OPER
SQL>

How to get all the new values in oracle trigger using :NEW or :OLD operator. like (:NEW.*)

I am trying to get all the new values and old values in trigger before inserting it to another table but i could just get the specific value not the all the data.
I tried with :NEW.* and :OLD.* it works fine for postgress but not for oracle.
CREATE OR REPLACE TRIGGER trg_customer_tbl_ib01
BEFORE INSERT OR UPDATE OR DELETE ON TEST.customer_test
FOR EACH ROW
DECLARE
vAction VARCHAR2(4000) := null;
vFound INT := null;
vREC TEST.CUSTOMER_TEST%ROWTYPE;
BEGIN
IF INSERTING OR UPDATING THEN
select :NEW.* into vREC FROM DUAL;
ELSIF DELETING THEN
select :OLD.* into vREC FROM DUAL;
END IF;
IF DELETING THEN
INSERT INTO TEST.CUSTOMER_TEST_AUDIT SELECT
CUSTOMER_TEST_AUDIT_id_seq.NEXTVAL,'D',SYSDATE, vREC.* FROM DUAL;
ELSIF UPDATING THEN
INSERT INTO TEST.CUSTOMER_TEST_AUDIT SELECT
CUSTOMER_TEST_AUDIT_id_seq.NEXTVAL,'U',SYSDATE, vREC.*FROM DUAL;
ELSIF INSERTING THEN
INSERT INTO TEST.CUSTOMER_TEST_AUDIT SELECT
CUSTOMER_TEST_AUDIT_id_seq.NEXTVAL,'I',SYSDATE, vREC.* FROM DUAL;
END IF;
END;
I am getting error :
Error(10,8): PLS-00049: bad bind variable 'NEW.'
Well, don't be impatient. Don't use shortcuts.
Test case:
SQL> create table test as select * From dept;
Table created.
SQL> create table test_log as select * From dept where 1 = 2;
Table created.
SQL> alter table test_log add (id number, action varchar2(1), datum date);
Table altered.
SQL> create sequence seqlog;
Sequence created.
SQL>
Trigger. Reference each column separately. The fact that :NEW.* works in Postgres doesn't mean it works in Oracle.
SQL> create or replace trigger trg_test
2 before insert or update or delete on test
3 for each row
4 begin
5 if updating then
6 insert into test_log (deptno, dname, loc, id, action, datum)
7 values
8 (:old.deptno, :old.dname, :old.loc, seqlog.nextval, 'U', sysdate);
9 insert into test_log (deptno, dname, loc, id, action, datum)
10 values
11 (:new.deptno, :new.dname, :new.loc, seqlog.nextval, 'U', sysdate);
12 elsif inserting then
13 insert into test_log (deptno, dname, loc, id, action, datum)
14 values
15 (:new.deptno, :new.dname, :new.loc, seqlog.nextval, 'I', sysdate);
16 elsif deleting then
17 insert into test_log (deptno, dname, loc, id, action, datum)
18 values
19 (:old.deptno, :old.dname, :old.loc, seqlog.nextval, 'D', sysdate);
20 end if;
21 end;
22 /
Trigger created.
SQL>
Testing:
SQL> alter session set nls_Date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> update test set loc = 'Croatia' where deptno = 10;
1 row updated.
SQL> delete from test where deptno = 20;
1 row deleted.
SQL> insert into test (deptno, dname, loc) values (99, 'IT', 'Zagreb');
1 row created.
SQL> select * From test_log order by id;
DEPTNO DNAME LOC ID A DATUM
---------- -------------- ------------- ---------- - -------------------
10 ACCOUNTING NEW YORK 5 U 20.05.2019 21:44:33
10 ACCOUNTING Croatia 6 U 20.05.2019 21:44:33
20 RESEARCH DALLAS 7 D 20.05.2019 21:44:43
99 IT Zagreb 8 I 20.05.2019 21:44:52
SQL>

insert record into table on exception

Here is my scenario:
I want to write a procedure in oracle, there are four tables, tab1, tab2, tab3, err. tab1 has some data in two columns (id number, name varchar(250)), while others are empty.
schema for tab2 is
(id number, name varchar(50)), for tab3 (id number, name varchar(250)).
I want to insert data from tab1 to tab2 and when there is exception like name is greater than varchar(50), it will insert into tab3 and also insert error message into err table.
So all record from tab1 should be inserted into tab2 and tab3 accordingly using exception handling.
Here is what I tried but failed.
CREATE OR REPLACE
PROCEDURE exception_handler
IS
vSqlErr VARCHAR2(200) ;
vSqlCode VARCHAR2(5) ;
id2 NUMBER;
name2 VARCHAR(250);
BEGIN
INSERT ALL INTO tab3 VALUES
(id, name
)
SELECT * FROM tab1 t;
EXCEPTION
WHEN OTHERS THEN
INSERT INTO tab2 VALUES
(id, name
);
vSqlErr := SUBSTR(sqlerrm, 1, 200) ;
vSqlCode := SUBSTR(SQLCODE, 1, 5) ;
INSERT INTO err VALUES
(vSqlErr, vSqlCode
) ;
COMMIT ;
RAISE;
END;
This is just a simple demonstration based on your inputs in the question. Better go for BULK processing and SQL%BULK_EXCEPTIONS. And don't use WHEN OTHERS blindly.
Let's say you have an EMP table, and you have a check constraint on employee name as not more than 5 characters. There is an EMP_ERR table to log the error values and error message. Lets see a test case:
SQL> DROP TABLE emp_new PURGE;
Table dropped.
SQL> CREATE TABLE emp_new AS
2 SELECT * FROM emp WHERE 1 =2;
Table created.
SQL> ALTER TABLE emp_new ADD CONSTRAINT check_ename CHECK(LENGTH(ename)<=5);
Table altered.
SQL> DROP TABLE emp_err PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE emp_err
2 (
3 empno NUMBER,
4 ename VARCHAR2(100),
5 err_msg VARCHAR2(250)
6 );
Table created.
SQL> CREATE OR REPLACE
2 PROCEDURE p
3 (
4 v_empno NUMBER,
5 v_ename VARCHAR2
6 )
7 IS
8 vSqlErr VARCHAR2(200) ;
9 vSqlCode VARCHAR2(5) ;
10 empno2 NUMBER;
11 ename2 VARCHAR2(250);
12 BEGIN
13 INSERT INTO emp_new
14 (empno, ename
15 ) VALUES
16 (v_empno, v_ename
17 );
18 COMMIT;
19 EXCEPTION
20 WHEN OTHERS THEN
21 vSqlErr := SUBSTR(sqlerrm, 1, 200) ;
22 vSqlCode := SUBSTR(SQLCODE, 1, 5) ;
23 INSERT
24 INTO emp_err
25 (
26 empno,
27 ename,
28 err_msg
29 )
30 VALUES
31 (
32 v_empno,
33 v_ename,
34 vSqlErr
35 ||' - '
36 ||vSqlCode
37 );
38 COMMIT ;
39 raise;
40 END;
41 /
Procedure created.
Lets execute the procure with ename value as more than 5 characters, so that it raises an error, and we expect a row to be inserted into the emp_err table.
SQL> exec p(1, 'abcdef');
BEGIN p(1, 'abcdef'); END;
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.CHECK_ENAME) violated
ORA-06512: at "SCOTT.P", line 38
ORA-06512: at line 1
So, the error is raised. Lets see if it is logged in the error table.
SQL> column ename format a10
SQL> column err_msg format a100
SQL> set linesize 150
SQL> select * from emp_err;
EMPNO ENAME ERR_MSG
---------- ---------- ----------------------------------------------------------------
1 abcdef ORA-02290: check constraint (SCOTT.CHECK_ENAME) violated - -2290
SQL>
We have the error details logged.

Resources