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

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>

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, how do i create stored procedure to insert value in 2 tables

I am trying to insert below details in two table. but it shows error. where i am wrong?
create or replace PROCEDURE ADD_customer_order(
customer_id in varchar, Shipping_id_arg in number,
order_date_arg in date, Total_price_arg in decimal,
inventory_id_arg in number, order_quantity_arg in number)
AS
BEGIN
INSERT INTO customer_order (customer_id,Shipping_id,Order_date,total_price) VALUES(customer_id_arg,Shipping_id_arg,order_date_arg, total_price_arg);
insert into order_details (inventory_id,order_quantity) values(scope_identity(),inventory_id_arg,order_quantity_arg);
END;
It helps if you format it nicely.
create or replace procedure add_customer_order(
customer_id in varchar2, shipping_id_arg in number,
order_date_arg in date , total_price_arg in decimal,
inventory_id_arg in number , order_quantity_arg in number)
as
begin
insert into customer_order
(customer_id , shipping_id , order_date , total_price)
values
(customer_id_arg, shipping_id_arg, order_date_arg, total_price_arg);
insert into order_details
( inventory_id , order_quantity)
values
(scope_identity(), inventory_id_arg, order_quantity_arg);
end;
Doing so, you easily note that the second INSERT is invalid, as you're inserting 3 values into 2 columns:
insert into order_details
( inventory_id , order_quantity)
values
(scope_identity(), inventory_id_arg, order_quantity_arg);
Either remove scope_identity() (what is it?), or include additional column into the column list you're inserting into.
After reading your comment, it seems that returning clause might help. See the following example (somewhat simpler than yours; didn't feel like typing that much). Trigger is used to auto-increment ORDER_ID column. in CUSTOMER_ORDER table (I'm on 11g XE; don't have identity columns here).
SQL> create table customer_order (order_id number, customer_id number);
Table created.
SQL> create table order_details (order_id number, inventory_id number);
Table created.
SQL> create sequence seqo;
Sequence created.
SQL> create or replace trigger trg_co
2 before insert on customer_order
3 for each row
4 begin
5 :new.order_id := seqo.nextval;
6 end;
7 /
Trigger created.
Procedure: note local variable declared in line #4 and returning clause in line #7:
SQL> create or replace procedure p_test
2 (par_customer_id in number, par_inventory_id in number)
3 is
4 l_order_id customer_order.order_id%type;
5 begin
6 insert into customer_order (customer_id) values (par_customer_id)
7 returning order_id into l_order_id;
8
9 insert into order_details (order_id, inventory_id)
10 values (l_order_id, par_inventory_id);
11 end;
12 /
Procedure created.
Testing:
SQL> exec p_test(100, 200);
PL/SQL procedure successfully completed.
SQL> exec p_test (235, 2230);
PL/SQL procedure successfully completed.
SQL> select * From customer_order;
ORDER_ID CUSTOMER_ID
---------- -----------
1 100
2 235
SQL> select * From order_details;
ORDER_ID INVENTORY_ID
---------- ------------
1 200
2 2230
SQL>
The same ORDER_ID value is used in both tables.

Warning: Trigger created with compilation errors

I am trying to create a trigger in oracle like blow:
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT ON VC_PART_LIST FOR EACH ROW
BEGIN
SELECT VC_PART_LIST_SEQ.NEWTVAL INTO :new.SEQ_ID FROM dual;
END;
/
anyone can help me?Thx.
In sequence to fetch the next value use NEXTVAL . Below is the correct syntax
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT
ON VC_PART_LIST
FOR EACH ROW
BEGIN
SELECT VC_PART_LIST_SEQ.NEXTVAL INTO :new.SEQ_ID FROM DUAL;
END;
There are few issues in your code.
1) VC_PART_LIST_SEQ.NEWTVAL there is nothing called NEWTVAL in Oracle. It should be Nextval.
2) There is no need for a SQL statement.
See demo:
Trigger:
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT
ON VC_PART_LIST
FOR EACH ROW
BEGIN
:new.SEQ_ID := VC_PART_LIST_SEQ.NEXTVAL;
END;
/
Table:
create table VC_PART_LIST (SEQ_ID number,
VC_VTC varchar2(20), PART_NO varchar2(20), CONSUME_QTY varchar2(20));
Sequence
create sequence VC_PART_LIST_SEQ start with 1 increment by 1;
Insertion:
INSERT INTO VC_PART_LIST (VC_VTC, PART_NO, CONSUME_QTY)
VALUES ('6175SMFGD132000D', 'SC25R136', '11');
Commit;
Selection:
SQL> select * from VC_PART_LIST;
SEQ_ID VC_VTC PART_NO CONSUME_QTY
---------- -------------------- -------------------- --------------------
1 6175SMFGD132000D SC25R136 11

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>

Count of table before and after insert inside a trigger

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.

Resources