Create Trigger in Oracle, in order to check two Dates - oracle

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>

Related

How to automate sql file execution?

We have CICD in place from long time for our application. Now, we are planning to automate DB script execution as well.
PFB the requirement.
For example, we have create_tables.sql and alter_tables.sql files.
create_tables.sql contains:
CREATE TABLE EMPLOYEE_DETAILS (
EMP_ID VARCHAR2(128 CHAR) NOT NULL,
FIRSTNAME VARCHAR2(128 CHAR),
LASTNAME VARCHAR2(128 CHAR),
CONSTRAINT REQUESTSUBMITTERS_PK PRIMARY KEY ( EMP_ID )
);
alter_tables.sql contains:
--v0.1
ALTER TABLE EMPLOYEE_DETAILS MODIFY FIRSTNAME VARCHAR2(256);
COMMIT;
--v0.2
ALTER TABLE EMPLOYEE_DETAILS MODIFY FIRSTNAME VARCHAR2(256) NOT NULL;
ALTER TABLE EMPLOYEE_DETAILS MODIFY LASTNAME VARCHAR2(256) NOT NULL;
COMMIT;
--v0.3
ALTER TABLE EMPLOYEE_DETAILS ADD EMAIL VARCHAR2(256) NOT NULL;
COMMIT;
Assume that we have an environments ABC. This environment has all changes available in create_tables.sql file but only --v0.1 change from alter_tables.sql file.
Now, we just want to promote JUST --v0.2 change from alter_tables.sql file to this environment. Please note that we don't want to promote changes --v0.1 and --v0.3 from alter_tables.sql file.
So, in general there will be --v0.xxx (a unique incremental indicator) in each sql file to specify which sql statements to execute.
Can you please let me know what could be the way to automate with this approach.
Also, please feel free to suggest any other solution which you think is better than above one.
First of all, ALTER is a DDL and it implicitly commits before and after executing that command, so - explicitly COMMITting is pretty much useless.
From my point of view, your approach is too difficult to apply as you have all the changes stored in the same file so you'd have to parse it and extract only some - desired - parts of it to be executed.
How about creating a set of tables that contains what to apply? Here's an example:
SQL> create table t_change_master
2 (change number constraint pk_cha primary key,
3 change_date date,
4 cb_apply number(1) default 0 not null,
5 applied_date date
6 );
Table created.
SQL> create table t_change_detail
2 (id number constraint pk_chadet primary key,
3 change number constraint fk_chadet_cha references t_change_master (change),
4 command varchar2(200),
5 cb_ok number(1),
6 error varchar2(200)
7 );
Table created.
Some sample data:
SQL> insert into t_change_master (change, change_date, cb_apply)
2 select 1, date '2022-06-25', 1 from dual union all
3 select 2, date '2022-06-26', 0 from dual union all
4 select 3, date '2022-06-28', 0 from dual;
3 rows created.
Note that ID = 1 and 4 contain duplicate commands:
SQL> insert into t_change_detail (id, change, command)
2 select 1, 1, 'alter table test add firstname varchar2(20)' from dual union all
3 select 2, 1, 'alter table test modify firstname varchar2(15)' from dual union all
4 select 3, 2, 'alter table test add lastname varchar2(20)' from dual union all
5 select 4, 2, 'alter table test add firstname varchar2(30)' from dual union all
6 select 5, 3, 'alter table test add address varchar2(30)' from dual;
5 rows created.
SQL>
This is table that will be used in this playground:
SQL> create table test (id number);
Table created.
Procedure that does the job; in two nested loops, it reads rows from the master table which are scheduled to run (cb_apply = 1) but have not been ran yet (applied_date is null). Inner loop just reads what to do, executes it and logs whether it was successful or not.
Basically, you'd schedule its execution using DBMS_SCHEDULER (or DBMS_JOB, depending on your database version):
SQL> create or replace procedure p_change is
2 l_err varchar2(200);
3 begin
4 for cur_m in (select change
5 from t_change_master
6 where cb_apply = 1
7 and applied_date is null
8 )
9 loop
10 for cur_d in (select id, command
11 from t_change_detail
12 where change = cur_m.change
13 )
14 loop
15 begin
16 dbms_output.put_line(cur_d.command);
17 execute immediate cur_d.command;
18
19 update t_change_detail set
20 cb_ok = 1
21 where id = cur_d.id;
22 exception
23 when others then
24 l_err := sqlerrm;
25 update t_change_detail set
26 cb_ok = 0,
27 error = l_err
28 where id = cur_d.id;
29 end;
30 end loop;
31 update t_change_master set
32 applied_date = sysdate
33 where change = cur_m.change;
34 end loop;
35 end;
36 /
Procedure created.
SQL>
OK, let's try it. Setting date format, just to know what is what:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> set serveroutput on;
SQL> begin
2 p_change;
3 end;
4 /
alter table test add firstname varchar2(20)
alter table test modify firstname varchar2(15)
PL/SQL procedure successfully completed.
SQL> select * from t_change_master;
CHANGE CHANGE_DATE CB_APPLY APPLIED_DATE
------- ------------------- ---------- -------------------
1 25.06.2022 00:00:00 1 05.07.2022 17:50:01
2 26.06.2022 00:00:00 0
3 28.06.2022 00:00:00 0
SQL> select * from t_change_detail;
ID CHANGE COMMAND CB_OK ERROR
--- ------- ---------------------------------------------- ------ ----------------------------------------
1 1 alter table test add firstname varchar2(20) 1
2 1 alter table test modify firstname varchar2(15) 1
3 2 alter table test add lastname varchar2(20)
4 2 alter table test add firstname varchar2(30)
5 3 alter table test add address varchar2(30)
SQL>
Let's now run change = 2:
SQL> update t_change_master set cb_apply = 1 where change = 2;
1 row updated.
SQL> begin
2 p_change;
3 end;
4 /
alter table test add lastname varchar2(20)
alter table test add firstname varchar2(30)
PL/SQL procedure successfully completed.
SQL> select * from t_change_master;
CHANGE CHANGE_DATE CB_APPLY APPLIED_DATE
------- ------------------- ---------- -------------------
1 25.06.2022 00:00:00 1 05.07.2022 17:50:01
2 26.06.2022 00:00:00 1 05.07.2022 17:50:58
3 28.06.2022 00:00:00 0
SQL> select * from t_change_detail;
ID CHANGE COMMAND CB_OK ERROR
--- ------- ---------------------------------------------- ------ ----------------------------------------
1 1 alter table test add firstname varchar2(20) 1
2 1 alter table test modify firstname varchar2(15) 1
3 2 alter table test add lastname varchar2(20) 1
4 2 alter table test add firstname varchar2(30) 0 ORA-01430: column being added already
exists in table
5 3 alter table test add address varchar2(30)
SQL>
Right; it kind of works.
Certainly, that piece of code (I wrote in a matter of minutes) could/should be improved, but that's just the general idea.
On the other hand, why wouldn't you do that using version control system, such as Git or Subversion? Good people developed these tools which are much, much more powerful that anything me (or you) could "invent" in such a short time. I guess it's worth to check these products.

Hi, I am trying to create a trigger, which automatically calculates col1 + col2 and inserts the sum into col3 after the data insertion

I am trying to create a trigger, which automatically calculates col1 + col2 and inserts the sum into col3 after the data insertion. For some reason there is an error,
Does anyone know how to achieve this?
CREATE OR REPLACE TRIGGER test_tr
AFTER INSERT ON test
FOR EACH ROW
DECLARE
test_one NUMBER(10);
test_two NUMBER(10);
BEGIN
SELECT random
INTO test_one
FROM test
WHERE id = id;
SELECT random2
INTO test_two
FROM test
WHERE id = id;
INSERT INTO test(test3) values ((test_one+test_two));
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
Don't select from the same table as trigger won't see it; table is mutating. Use pseudorecords.
SQL> create table test_one (random number, random2 number, test3 number);
Table created.
SQL> create or replace trigger test_tr
2 before insert on test_one
3 for each row
4 begin
5 :new.test3 := :new.random + :new.random2;
6 end;
7 /
Trigger created.
SQL> insert into test_One (random, random2)
2 select 1, 2 from dual union all
3 select 5, 3 from dual;
2 rows created.
SQL> select * from test_One;
RANDOM RANDOM2 TEST3
---------- ---------- ----------
1 2 3
5 3 8
SQL>
On the other hand, why bother with a trigger? Create a virtual column:
SQL> create table test_one
2 (random number,
3 random2 number,
4 test3 number as (random + random2) virtual
5 );
Table created.
SQL> insert into test_One (random, random2) values (3, 9);
1 row created.
SQL> select * from test_one;
RANDOM RANDOM2 TEST3
---------- ---------- ----------
3 9 12
SQL>
Or, don't create anything and calculate random + random2 when you need it.
Don't use a trigger, from Oracle 11gR1, you can use a virtual column:
ALTER TABLE test ADD test3 NUMBER GENERATED ALWAYS AS (test1 + test2);
Alternatively, when you want to display the test3 value then calculate it in the SELECT clause:
SELECT test1, test2, test1 + test2 AS test3 FROM test;
db<>fiddle here

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>

How to auto increment the id in oracle?

Is there any way to auto increment the id in oracle?
UPDATE: It's working but the sequence start in 2 instead of 1. I already set the sequence start with 1
If it possible not use the sequence but still id's will be auto increment?
Here:
CREATE TABLE tblname
(
fieldname_id number(25),
contract_number number(12) not null,
CONSTRAINT letter_status_pk PRIMARY KEY (fieldname_id, contract_number)
);
Sequence w/trigger:
create sequence fieldname_id_sequence start with 1
increment by 1
minvalue 1;
create trigger tr_tblname
before insert on tblname
for each row
begin
select fieldname_id_sequence.nextval into :NEW.fieldname_id from dual;
end;
insert data:
insert all
into tblname(contract_number,fieldname1,fieldname2,fieldname3,fieldname4,fieldname5,fieldname6,fieldname7,fieldname8,fieldname8,fieldname10,fieldname11) values(3300026224,'values','values','values','3/12/2014','values','values','3/18/2014','3/7/2014','values','values')
into tblname(contract_number,fieldname1,fieldname2,fieldname3,fieldname4,fieldname5,fieldname6,fieldname7,fieldname8,fieldname8,fieldname10,fieldname11) values (3300016335,'values','values','values','3/12/2014','values','values','3/18/2014','3/7/2014','values','values')
select 1 from dual;
you can model it easily with a sequence and a trigger:
Create sequence sequence_name start with value increment by value minvalue value maxvalue value;
First, let’s create an emp table with primary key constraint on emp_id column.
SQL> create table emp ( emp_id number(10),
fname varchar2(25),
lname varchar2(25),
constraint pk_emp_id PRIMARY KEY(emp_id)
);
Now let’s create a sequence.
SQL> Create sequence emp_sequence start with 1
increment by 1
minvalue 1
maxvalue 10000;
Now we have created a sequence object named emp_sequence with starting value as 1 and incrementing by 1 from 1 (minvalue) to 10000 (maxvalue)
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Darvin','Johnson'); SQL>
insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Mig','Andrews');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Alex','Martin');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Jon','paul');
SQL> insert into emp (emp_id,fname,lname) values(emp_sequence.nextval,'Yatin','Bones');
In emp_sequence.nextval where emp_sequence is the name of sequence we created above and nextval is a function that is used to assign the next number from emp_sequence to emp_id column in emp table.
Oracle 12c introduces IDENTITY COLUMNS.
SQL> CREATE TABLE new_identity_table
2 (
3 ID NUMBER GENERATED ALWAYS AS IDENTITY,
4 text VARCHAR2(50)
5 );
Table created.
SQL>
SQL> INSERT
2 INTO new_identity_table
3 (
4 text
5 )
6 VALUES
7 (
8 'This table has an identity column'
9 );
1 row created.
SQL> column text format A40;
SQL>
SQL> select * from new_identity_table;
ID TEXT
---------- ----------------------------------------
1 This table has an identity column
SQL>
Oracle creates a sequence to populate the identity column. You can find it named as ISEQ$$
SQL> select sequence_name, min_value, max_value, increment_by from user_sequences;
SEQUENCE_NAME MIN_VALUE MAX_VALUE INCREMENT_BY
-------------------- ---------- ---------------------------- ------------
ISEQ$$_93199 1 9999999999999999999999999999 1
SQL>
More more information about the identity columns, use the ALL_TAB_IDENTITY_COLS view.
SQL> SELECT table_name,
2 column_name,
3 generation_type,
4 identity_options
5 FROM all_tab_identity_cols
6 WHERE owner = 'LALIT'
7 ORDER BY 1, 2;
TABLE_NAME COLUMN_NAME GENERATION IDENTITY_OPTIONS
-------------------- --------------- ---------- --------------------------------------------------
NEW_IDENTITY_TABLE ID ALWAYS START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 9999999
999999999999999999999, MIN_VALUE: 1, CYCLE_FLAG: N
, CACHE_SIZE: 20, ORDER_FLAG: N
SQL>

Added date and modified date for Oracle table audit

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;

Resources