Count of table before and after insert inside a trigger - oracle

Is it possible to check the count of a table before any changes happen and the count after the insert and match them inside the same trigger?
for ex: old.count and new.count (before and after insert) ?

old.count and new.count (before and after insert)
Nothing stops you from using SELECT COUNT(*) in a before insert trigger. Of course, you won't do it in a after insert trigger, since a select count(*) on the same table on which an after trigger is defined would throw mutating table error. One way is autonomous transaction. But, in your case, it isn't that complex.
You could define a BEFORE INSERT TRIGGER and take the table count. The after insert count could be taken manually after the actual insert is done.
For example, I have a table t1 with one row. I have defined a before insert trigger on it, which would give me the table count before the insert happens.
SQL> DROP TABLE t1 PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE t1 (A NUMBER);
Table created.
SQL>
SQL> INSERT INTO t1 VALUES (1);
1 row created.
SQL>
SQL> SELECT * FROM t1;
A
----------
1
SQL>
SQL> CREATE OR REPLACE TRIGGER trg
2 BEFORE INSERT
3 ON t1
4 FOR EACH ROW
5
6 DECLARE
7 val number;
8 BEGIN
9 SELECT COUNT(*)
10 INTO val
11 FROM t1;
12
13 DBMS_OUTPUT.PUT_LINE('TABLE COUNT BEFORE INSERT = '||val);
14
15 END;
16 /
Trigger created.
SQL>
SQL> set serveroutput on
SQL> INSERT INTO t1 VALUES (1);
TABLE COUNT BEFORE INSERT = 1
1 row created.
SQL>
SQL> SELECT COUNT(*) FROM t1;
COUNT(*)
----------
2
SQL>
So, you see TABLE COUNT BEFORE INSERT = 1 and then after insert the count is 2.

Related

Update second column

I have a table "TEST_TABLE" with two columns TABLE_NAME and RECORD_COUNT.
enter image description here
We want to update the column RECORD_COUNT by taking the total records in table specified in TABLE_NAME.
You could do this with dynamic SQL but why be complicated?
Create a view
Create view my_tables as
Select
'table_1' as "table name",
count(*) as "rows"
From table_1
Add the following for each table
Union all
Select 'table_2',count(*) From table_2
You can then use the view like a table:
Select * from my_tables;
OK, but - why wouldn't you use information Oracle already provides for you? You should regularly gather statistics anyway, so:
SQL> execute dbms_stats.gather_schema_stats(ownname => 'SCOTT', estimate_percent => null);
PL/SQL procedure successfully completed.
and then fetch num_rows from user_tables:
SQL> select table_name, num_rows from user_tables where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
It can't be much worse than your attempt (due to possible frequent changes to tables' contents; inserts and deletes) because you'd collect your own data periodically (how often?) anyway.
If it has to be your way, then you'd use dynamic SQL, looping through all tables in your schema and actually count number of rows:
SQL> create table test_table
2 (table_name varchar2(30),
3 num_rows number);
Table created.
SQL> create or replace procedure p_test as
2 l_cnt number;
3 begin
4 execute immediate 'truncate table test_table';
5 for cur_R in (select table_name from user_tables) loop
6 execute immediate 'select count(*) from ' ||cur_R.table_name into l_Cnt;
7 insert into test_table (table_name, num_rows) values (cur_r.table_name, l_cnt);
8 end loop;
9 end;
10 /
Procedure created.
Running the procedure and viewing the result:
SQL> exec p_test;
PL/SQL procedure successfully completed.
SQL> select * From test_Table where rownum <= 10;
TABLE_NAME NUM_ROWS
-------------------- ----------
EMP 14
DEPT 4
BONUS 0
SALGRADE 5
DUMMY 1
TBL_ERROR 1
AUDIT_TAB 2
SOURCE_DET 3
EXAMPLE 1
FLIGHT 0
10 rows selected.
SQL>
Note that performance will suffer as number of tables and number of rows stored in them grows.
If I were you, I'd go for the first option.

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>

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>

Update ID if Sequence MAX_VALUE reached

I am using the following TRIGGER to Insert in my TEST Table:
create or replace
TRIGGER TRG_CYCLE
BEFORE INSERT ON TEST_CYCLE
FOR EACH ROW
BEGIN
IF :NEW.LOGID IS NULL
THEN SELECT SEQ_CYCLE.nextval INTO :NEW.LOGID from dual;
END IF;
END;
SEQ_CYCLE is the Sequence. Now I have to use the CYCLE option in order to start with 1 after the MAX_VALUE is reached. Since I don't want duplicate LOGIDs I want to do an UPDATE if the LOGID exists, if not an INSERT. Can I do this inside the Trigger?
If I understand well, you may need an INSTEAD OF trigger; to build such a trigger you need to create a VIEW over your table and build the trigger on this view.
For example, say you have:
create sequence SEQ_CYCLE maxValue 3 cycle nocache;
create table TEST_CYCLE ( logId number, someColumn varchar2(20));
You can create a view and a trigger over the view:
create or replace view v_test_cycle as select * from test_cycle;
create or replace trigger trgCycle
instead of insert on v_test_cycle
for each row
declare
vCheck number;
vSeqVal number := SEQ_CYCLE.nextVal;
begin
select count(*)
into vCheck
from v_test_cycle
where logId = vSeqVal;
--
if vCheck = 0 then
insert into test_cycle(logId, someColumn) values ( vSeqVal, :NEW.someColumn);
else
update test_cycle
set someColumn = :NEW.someColumn
where logId = vSeqVal;
end if;
end;
If you do the INSERTs on the view, this is what you get:
SQL> select * from test_cycle;
no rows selected
SQL> insert into v_test_cycle(someColumn) values('some value 1');
1 row created.
SQL> insert into v_test_cycle(someColumn) values('some value 2');
1 row created.
SQL> insert into v_test_cycle(someColumn) values('some value 3');
1 row created.
SQL> insert into v_test_cycle(someColumn) values('some value 4');
1 row created.
SQL> insert into v_test_cycle(someColumn) values('some value 5');
1 row created.
SQL> select * from test_cycle;
LOGID SOMECOLUMN
---------- --------------------
1 some value 4
2 some value 5
3 some value 3
SQL>

Oracle Procedure to Copy data view into table

I would need to copy or insert data from my_view (that is based on 3 joined tables). Before that, I've created table my_table based on my_view .
For the procedure I've used the below :
create or replace procedure view_copy(
my_view in varchar2,
my_table in varchar2)
is
begin
execute immediate 'insert into '||my_table||' (select * from '||my_view||')';
end;
The data are not copied to my_table. Is it possible to copy data from view to table? How can I write the procedure differently?
Your procedure works; if you can't find data in your target table, maybe you should check your view.
A simple test on your procedure:
SQL> create table table1 ( id1 number)
2 /
Table created.
SQL> create table table2 ( id2 number)
2 /
Table created.
SQL> create or replace view v_t1_t2 as select id1, id2 from table1 cross join table2
2 /
View created.
SQL> create table table1_2 ( id1 number, id2 number)
2 /
Table created.
SQL> create or replace procedure view_copy(
2 my_view in varchar2,
3 my_table in varchar2)
4 is
5 begin
6 execute immediate 'insert into '||my_table||' (select * from '||my_view||')';
7 end;
8 /
Procedure created.
SQL> insert into table1 values (1);
1 row created.
SQL> insert into table2 values (2);
1 row created.
SQL> exec view_copy('v_t1_t2', 'table1_2');
PL/SQL procedure successfully completed.
SQL> select * from table1_2;
ID1 ID2
---------- ----------
1 2

Resources