Added date and modified date for Oracle table audit - oracle

According to requirenments I need to support two mandatory columns for each table: ADDED_DATE, MODIFIED_DATE.
These are intended for DBA/auditing purposes.
Is it possible to make this stuff totally automatic, implicitly supported by DB itself when I'm inserting / updating records from within application?

create a trigger on the table (before update for each row).
SQL> create table foo (hi varchar2(10), added_date date, modified_date date);
Table created.
SQL> create trigger foo_auifer
2 before update or insert on foo
3 for each row
4 declare
5 begin
6 if (inserting) then
7 :new.added_date := sysdate;
8 elsif (updating) then
9 :new.modified_date := sysdate;
10 end if;
11 end;
12 /
Trigger created.
SQL> insert into foo (hi) values ('TEST');
1 row created.
SQL> insert into foo (hi) values ('TEST2');
1 row created.
SQL> update foo set hi = 'MODDED' where rownum = 1;
1 row updated.
SQL> alter session set nls_date_format='dd-mon-yyyy hh24:mi:ss';
Session altered.
SQL> select * from foo;
HI ADDED_DATE MODIFIED_DATE
---------- -------------------- --------------------
MODDED 07-nov-2012 15:28:28 07-nov-2012 15:28:39
TEST2 07-nov-2012 15:28:30
SQL>

create table "db_user"."my_table" (
...
"added_date" date default sysdate,
"modified_date" date default sysdate
)
/
create or replace
trigger "db_user"."trg_my_table_audit"
before update on my_table for each row
begin
:new.modified_date := sysdate;
end;

Related

How to duplicate records with triggers?

CREATE OR REPLACE TRIGGER "DUPLICATE_FOO" AFTER INSERT ON "FOO" FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
insert into remote_foo values(:new); -- can I do this?
EXCEPTION
-- TODO log somewhere
END;
Is there an elegant way to create a trigger that basically duplicates a record from one table to another?
I would like to avoid having to specify the fields of the table since it will mean that the trigger would have to be updated in case there are changes in the schema (the remote scheme would be updated of course). I have like a dozen of tables more.
All the examples I have found always specify the fields in the insert (:new.fieldX).
The keyword here is NOT to specify column names, right? In my opinion, you should because that's the only way you can control it.
the remote scheme would be updated of course
is kind of dangerous. WHAT IF it doesn't happen? "Of course" works until it does not work.
Sample tables (both are empty):
SQL> create table foo (id number, name varchar2(20));
Table created.
SQL> create table remote_foo as select * From foo where 1 = 2;
Table created.
If you use a trigger which is an autonomous transaction, then it won't see :new pseudorecord (as this is an autonomous transaction; right?); to this trigger, select * from foo where id = :new.id; won't return anything and remote_foo remains empty:
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into remote_foo select * from foo where id = :new.id;
8 commit;
9 end;
10 /
Trigger created.
SQL> insert into foo (id, name) values (1, 'Littlefoot');
1 row created.
SQL> select * from foo;
ID NAME
---------- --------------------
1 Littlefoot
SQL> select * from remote_foo; --> it remained empty
no rows selected
SQL>
Note that - if you specified columns - it would work (but that's not what you wanted):
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into remote_foo (id, name) values (:new.id, :new.name);
8 commit;
9 end;
10 /
Trigger created.
SQL> insert into foo (id, name) values (2, 'Bigfoot');
1 row created.
SQL> select * from foo;
ID NAME
---------- --------------------
2 Bigfoot
SQL> select * from remote_foo;
ID NAME
---------- --------------------
2 Bigfoot
SQL>
So, what to do? Switch to a statement-level trigger (instead of a row-level): it doesn't have to be autonomous, but has to have something that will prevent duplicates to be inserted - for example, a NOT EXISTS clause:
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 begin
4 insert into remote_foo
5 select * from foo a
6 where not exists (select null from remote_foo b
7 where b.id = a.id);
8 end;
9 /
Trigger created.
SQL> insert into foo (id, name) values (1, 'Littlefoot');
1 row created.
SQL> insert into foo (id, name)
2 select 2, 'Bigfoot' from dual union all
3 select 3, 'anat0lius' from dual;
2 rows created.
Result:
SQL> select * from foo;
ID NAME
---------- --------------------
2 Bigfoot
3 anat0lius
1 Littlefoot
SQL> select * from remote_foo;
ID NAME
---------- --------------------
1 Littlefoot
3 anat0lius
2 Bigfoot
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.

Create Trigger in Oracle, in order to check two Dates

I would like to create a Trigger in Oracle, in order to check the Current Date with the Date that is stored in a specific column and if they are equal, I would like to change a boolean value on the same table. I am new to Oracle and especially on creating triggers. Any help is much appreciated.
CREATE TABLE DISCIPLINARYAUDIT
(
DISCIPLINARYAUDITID NUMBER(19, 0) NOT NULL
, DISCIPLINARYAUDITTYPE VARCHAR2(255 CHAR) NOT NULL
, REVOCATIONORCOMPLETION NUMBER(1, 0)
, STARTDATE DATE
, ENDDATE DATE
)
When endDate equals with current Date, RevocationORCompletion must chenage from 0 to 1.
Thank you in advance.
Here's an example.
Date format and today's date:
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
Sample table:
SQL> create table test (id number, specific_column date, boolean_column varchar2(10));
Table created.
SQL> insert into test values (1, trunc(sysdate), 'FALSE');
1 row created.
SQL> insert into test values (2, trunc(sysdate) - 2, 'FALSE');
1 row created.
SQL> select * from test order by id;
ID SPECIFIC_C BOOLEAN_CO
---------- ---------- ----------
1 04.06.2020 FALSE
2 02.06.2020 FALSE
Trigger:
SQL> create or replace trigger trg_biu_test
2 before insert or update on test
3 for each row
4 begin
5 if :new.specific_column = trunc(sysdate) then
6 :new.boolean_column := 'TRUE';
7 end if;
8 end;
9 /
Trigger created.
Testing:
SQL> update test set id = 3 where id = 1;
1 row updated.
SQL> update test set id = 5 where id = 2;
1 row updated.
SQL> select * from test;
ID SPECIFIC_C BOOLEAN_CO
---------- ---------- ----------
3 04.06.2020 TRUE
5 02.06.2020 FALSE
SQL>

Oracle SQL - ORA-04079: invalid trigger insert

I am trying to create a trigger that runs when I insert in table 'cuenta' and make an insert in table 'cuenta_log', one of the values of this insert is gotten by accept input.
create or replace trigger trigger_new_account
AFTER INSERT ON cuenta FOR EACH ROW
accept vstring prompt "Please enter your name: ";
declare v_line varchar2(50);
begin
v_line:= 'Hello '||'&vstring';
insert into cuentas_log (fecha,cuenta,cliente)
values (now(),:new.idcuenta,v_line);
end;
cuenta_log structure is like that:
cuenta_log
("FECHA" DATE DEFAULT (sysdate),
"CUENTA" NUMBER(38,0),
"CLIENTE" VARCHAR2(50 BYTE)
)
Trigger fires on certain event; yours, after insert into table named cuenta. It doesn't work interactively with end users. You can't expect it to prompt anyone to enter anything. Besides, accept is a SQL*Plus command, it won't work in PL/SQL.
So, here's what you might have done: sample tables first:
SQL> create table cuenta (fecha date, cuenta number);
Table created.
SQL> create table cuenta_log (fecha date, cuenta number, cliente varchar2(30));
Table created.
Trigger:
SQL> create or replace trigger trigger_new_account
2 after insert on cuenta
3 for each row
4 begin
5 insert into cuenta_log(fecha, cuenta, cliente)
6 values
7 (sysdate, :new.cuenta, 'Hello ' || user);
8 end;
9 /
Trigger created.
Testing:
SQL> insert into cuenta (fecha, cuenta) values (sysdate, 100);
1 row created.
SQL> select * From cuenta;
FECHA CUENTA
------------------- ----------
11.05.2020 12:31:17 100
SQL> select * From cuenta_log;
FECHA CUENTA CLIENTE
------------------- ---------- ------------------------------
11.05.2020 12:31:17 100 Hello SCOTT
SQL>

How to add extra column in the INTO section of SQL Trigger

How to add an extra column (say column_2) in the below INTO section of my code along with my Column_1. I assume we can do that by adding comma (,) and just add column_2 (like this INTO :new.Column_1, new.column_2). I'm missing something?
create or replace trigger trigger_name
BEFORE INSERT
ON table_name
FOR EACH ROW
BEGIN
SELECT SEQUENCE_NUMBER.NEXTVAL
INTO :new.Column_1
FROM dual;
END;
It is easy to confirm whether you are right (or wrong). I hope you got the answer during the past 6 hours. If not, here's an example:
SQL> create table test
2 (id number,
3 datum date);
Table created.
SQL> create sequence seq_test;
Sequence created.
SQL> create or replace trigger trg_bi_test
2 before insert on test
3 for each row
4 begin
5 select seq_test.nextval, sysdate
6 into :new.id, :new.datum
7 from dual;
8 end;
9 /
Trigger created.
SQL> insert into test (id) values (-1);
1 row created.
SQL> select * From test;
ID DATUM
---------- -------------------
1 21.06.2019 21:54:08
SQL>

Resources