Oracle trigger to increment value in another table after inserted in other - oracle

How to create trigger or procedure to add incrementally +1 in column VALUE in TABLE1 every single time when something is inserted in TABLE2 ?
Lets say that we start from VALUE.TABLE1 = 1 and I insert record in TABLE2. After that I should see 2 in VALUE column in TABLE1.
Then someone else is inserting record in TABLE2 and that procedure/trigger should add yet again +1 in VALUE and now it should be 3
etc.
DB is Oracle 11
Thanks.

Don't use table table1 and don't use a trigger.
Use a view instead:
create or replace view v_table1 as
select count(*) + 1 as value
from table2;

CREATE TRIGGER table2_trg
AFTER INSERT
ON table2
FOR EACH ROW
BEGIN
UPDATE table1 set value = value + 1;
END;
Or
CREATE or REPLACE TRIGGER TABLE2_trg
FOR INSERT
ON TABLE2
COMPOUND TRIGGER
cnt PLS_INTEGER := 0;
AFTER EACH ROW IS
BEGIN
cnt := cnt + 1;
END AFTER EACH ROW ;
AFTER STATEMENT IS
BEGIN
UPDATE table1 SET value = value + cnt;
END AFTER STATEMENT;
END;
I am not sure it is better or not and how it will work when some rows in table2 are dropped due to exceptions at the time of insertion.
It's better to use view if don't bother about deleted rows in table2.

Related

Compound Trigger to turn a single row into a multirow

`Basically, i need to create something that when the user insert a value on a table, it turns into a multi insert, he will say the number of rows that will become in a part of table.
But i have 0 ideia from were begin.
My table have COMPMOV - INT: is the number of rows will return.
VLRPROV - NUMBER:
DTVENC - DATE:
PRAZO - INT:
PARCELA - INT:
i started from a another trigger that i created before, but this one i just fill a field that is blank when the user insert.`
CREATE OR replace TRIGGER "TRG_INC_USU_OBSMULTI"
FOR INSERT OR UPDATE
ON AD_OBSMULTI
COMPOUND TRIGGER TYPE R_OBS_TYPE IS RECORD (
OBS_ID AD_OBSMULTI.ID%TYPE,
OBS_IDOBS AD_OBSMULTI.IDOBS%TYPE,
OBS_CODUSU AD_OBSMULTI.CODUSU%TYPE
);
TYPE T_OBS_TYPE IS TABLE OF R_OBS_TYPE INDEX BY PLS_INTEGER;
T_OBS T_OBS_TYPE;
AFTER EACH ROW IS BEGIN
T_OBS (T_OBS.COUNT + 1).OBS_IDOBS := :NEW.IDOBS;
T_OBS (T_OBS.COUNT).OBS_ID := :NEW.ID;
T_OBS (T_OBS.COUNT).OBS_CODUSU := :NEW.CODUSU;
END AFTER EACH ROW;
AFTER STATEMENT IS L_CODUSU AD_OBSMULTI.CODUSU%TYPE;
BEGIN
SELECT
STP_GET_CODUSULOGADO INTO L_CODUSU
FROM
DUAL;
FOR indx IN 1..T_OBS.COUNT
LOOP
IF T_OBS(indx).CODUSU IS NULL THEN
UPDATE
AD_OBSMULTI
SET
CODUSU = L_CODUSU
WHERE
ID = T_OBS(indx).OBS_ID
AND IDOBS = T_OBS(indx).OBS_IDOBS;
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
A trigger can modify the values of the dml statement (insert/update/delete) only. An additional transaction on the same table within a trigger on that table is generally a bad idea.
But here is a workaround.
Create a table
Create a view on that table
Create an instead of trigger on the view
Insert into the view
Example:
CREATE TABLE MAIN_TABLE (id number);
Table MAIN_TABLE created.
CREATE OR REPLACE VIEW MAIN_VIEW AS
SELECT id, 0 as new_rows FROM MAIN_TABLE;
View MAIN_VIEW created.
INSERT INTO main_view (id) values (100);
1 row inserted.
It is possible to insert into the view. Since there is a 1:1 relationship with the main_table this will insert a row in MAIN_TABLE
SELECT COUNT(*) FROM main_table;
COUNT(*)
----------
1
Now create the instead of trigger on MAIN_VIEW
CREATE OR REPLACE TRIGGER main_view_ioi
INSTEAD OF INSERT
ON main_view
FOR EACH ROW
BEGIN
FOR i IN 1 .. :NEW.NEW_ROWS LOOP
INSERT INTO main_table (id) VALUES (i);
END LOOP;
END;
/
Trigger MAIN_VIEW_IOI compiled
INSERT INTO main_view (id,new_rows) values (1,5);
1 row inserted.
Note that this will say "1 row inserted" because only 1 row was inserted in MAIN_VIEW. However, the instead of trigger created 5 rows in MAIN_TABLE.
SELECT * FROM main_table;
ID
----------
100
1
2
3
4
5

How to rollback a column and its trigger in plsql?

I have a litte task. Firstly I added a column in my table with specific constraints. Then I added a trigger for other jobs.
But I need a rollbacksql and have no idea what to proceed. Can anybody help or give an advice about it? I am adding my sql snippet.
ALTER TABLE FCBSADM.GL_DEF ADD GL_TP NUMBER;
ALTER TABLE FCBSADM.GL_DEF ADD CONSTRAINTS CH_COL CHECK (GL_TP between 1 and 10);
CREATE OR REPLACE TRIGGER GL_DEF_GL_TP_TRG
BEFORE INSERT OR
DELETE OR
UPDATE OF CDATE, CMPNY_DEF_ID, CUSER, DESCR, GL_DEF_ID, MNY_TP_ID, ST, UDATE, UUSER, GL_TP
ON FCBSADM.GL_DEF
FOR EACH ROW
DECLARE
cnt number := 0;
BEGIN
IF INSERTING
THEN
IF :NEW.GL_TP = 2
THEN
SELECT 1 into cnt from dual where exists( select *
FROM LOOKUP_GLCODE_IND_CEZA lookup
WHERE lookup.indirim_glcode = :NEW.gl_Def_id);
IF (cnt = 1) THEN
raise_application_error
(-20101, 'Please insert record into LOOKUP_GLCODE_IND_CEZA before inserting GL_DEF');
END IF;
END IF;
END IF;
END;
What do you want to rollback? Adding a column and creating a trigger? If so, drop them, both.
alter table gl_def drop column gl_tp;
drop trigger gl_def_gl_tp_trg;
A trigger and newly added table can be rolled back only by using drop and alter statements. This is ok if its being done inside a script that executes only a few times. But is highly inefficient if both drop and alter are called frequently for n number of records.

While INSERT got error PLS-00904: stud.col3 is invalid identifier

In my stored procedure I want if the value of col1 & col2 match with employee then insert the unique record of the employee. If not found then match the value of col1, col2 & col3 with employee match then insert the value. If also not found while match all these column then insert the record by using another column.
Also one more thing that I want find list of values like emp_id by passing the another column value and if a single record can not match then make emp_id as NULL.
Also I want to insert one record at a time after match with txt along with others table having data like emp.
create or replace procedure sp_ex
as
cursor c1 is select * from txt%rowtype;
v_col1 tbl1.col1%type;
type record is table of txt%rowtype; --Staging table
v_rc record := record();
begin
open c1;
loop
fetch c1 bulk collect into v_rc limit 1000;
loop
for i in 1..v_rc.count loop
select col1 into v_col1 from tbl1
where exists (select col1 from tbl1 where tbl1.col1 = emp.col1);
insert
when txt.col1 = emp.col1 and txt.col2 = stud.col2 then
into main_table(columns) values(v_rc(i).col1, ...)
when txt.col1 = emp.col1 and txt.col2 = stud.col2 and txt.col3 = stud.col3 then
into main_table(columns) values(v_rc(i).col1, ...)
else
insert into main_table(columns) values(v_rc(i).col1, ...)
select * from txt;
end loop;
exit when v_rc.count < limit;
end loop;
close c1;
end sp_ex;
While emp, stud are the different tables where i have to match with txt.
In that Stored Proc I want to load data from txt into main_table in batch processing mode. The data would be match one by one record then after if matching condition match then load into the main table. How can i create the stored proc so that the Data will load by above logic one by one in batch processing. Could you please help me to share your idea. Thanks
The syntax seems to be rather mixed up.
Multi-table insert is like this:
insert all -- alternatively, "insert first"
when dummy = 'X' then
into demo (id) values (1)
when dummy = 'Y' then
into demo (id) values (2)
else
into demo (id) values (3)
select * from dual;
Or perhaps you wanted a PL/SQL case statement:
case
when dummy = 'X' then
insert into demo (id) values (1);
when dummy = 'Y' then
insert into demo (id) values (2);
else
insert into demo (id) values (3);
end case;
Instead there seems to be a mixture of the two.
Also there is a missing end loop, and an implicit cursor (select col1 from tbl1) with no into clause.

How to insert values from one table into another and then update the original table?

Using Oracle, is there a way to insert values from one table into another table, then take an identity value from the inserted table and update a column in the original?
TABLE_1 is empty
ID VALUE
-----------
Values from TABLE_2 ...
ID VALUE
-----------
0 Val 1
0 Val 2
0 Val 3
...get inserted into TABLE_1 (with an Identity column)
ID VALUE
-----------
1 Val 1
2 Val 2
3 Val 3
And then updates TABLE_2 with the IDs
ID VALUE
-----------
1 Val 1
2 Val 2
3 Val 3
You need to get procedural for such a requirement. This solution uses SELECT ... FOR UPDATE which locks the source table, to prevent another session nabbing the record we want to give the new ID. It also gives us the WHERE CURRENT OF syntax, which makes it easy to identify the record we need to update.
This solution supposes the existence of a sequence for populating the identity column. There are other options available to us (including auto-increments in 12C) but the RETURNING clause is the key to snagging the new value.
declare
cursor c2 is
select * from table2
for update of id;
r2 c2%rowtype;
new_id t1.id%type;
begin
open c2;
loop
fetch c2 in r2;
exit when c2%notfound;
/* new record */
insert into t1
values (t1_seq.nextval, t2.value)
returning t1.id into new_id;
/* update existing record with id*/
update t2
set id = new_id
where current of c2;
end loop;
commit;
end;
/
This solution is Row-By-Row" it is the easiest way to make sure that the new T1.ID gets applied to the correct row in T2. If T1 is small and/or this is a on-off exercise that's probably fine. But if performance is a concern there are tunings available.
If in table 2 are a lot of rows i recommend you to use bulk collect.
It will help you to improve the performance on database. Like this:
declare
type type_table2 is table of table2%rowtype index by binary_integer;
vt_table2 type_table2;
cursor cur_table2 is select * from table2;
begin
open cur_table2;
loop
fetch cur_table2 bulk collect into vt_table2 limit 500;
for i in 1..vt_table2.count loop
begin
insert into table1
values(i, vt.table2(i).value);
update table2
set id = i
where current of cur_table2;
exception when other then
excp := SQLERRM;
dbms_output.put_line('Error:'||excp);
end;
end loop;
exit when cur_table%notfound;
end loop;
close cur_table2;
commit;
exception when other then
excp := SQLERRM;
dbms_output.put_line('Error:'||excp);
end;

PL SQL batch processing with insert from select

I have to move the data from table A to table B (they have almost the same fields).
What I have now is a cursor, that iterates over the records that has to be moved, insert one record in the destination table and updates the is_processed field in the source table.
Something like:
BEGIN
FOR i IN (SELECT *
FROM A
WHERE A.IS_PROCESSED = 'N')
LOOP
INSERT INTO B(...) VALUES(i....);
UPDATE A SET IS_PROCESSED = 'Y' WHERE A.ID = i.ID;
COMMIT;
END LOOP;
END;
The questions is, how to do the same using INSERT FROM SELECT(without the loop) and then update IS_PROCESSED of all the moved rows?
There is no BULK COLLECT INTO for INSERT .. SELECT
May be you can try this. I don't think it's better than your LOOP.
DECLARE
TYPE l_src_tp IS TABLE OF t_source%ROWTYPE;
l_src_rows l_src_tp;
BEGIN
SELECT *
BULK COLLECT INTO l_src_rows
FROM t_source;
FORALL c IN l_src_rows.first .. l_src_rows.last
INSERT INTO t_dest (td_id, td_value)
VALUES (l_src_rows(c).ts_id, l_src_rows(c).ts_value);
FORALL c IN l_src_rows.first .. l_src_rows.last
UPDATE t_source
SET ts_is_proccesed = 'Y'
WHERE ts_id = l_src_rows(c).ts_id;
END;
If you reverse the order and first make update and then insert you can use:
DECLARE
ids sys.odcinumberlist;
BEGIN
UPDATE a SET is_processed = 'Y' WHERE is_processed = 'N' RETURNING id BULK COLLECT INTO ids;
INSERT INTO b (id) SELECT column_value id FROM TABLE(ids);
COMMIT;
END;
In the SELECT you can join the ids table and get other data from other tables you want to insert into b.
Hello I should prefer pure SQL rather than PLSQL. I don't know why another update statement is requires for this simpler task. Let me know if this helps.
INSERT INTO <new table>
SELECT col1,col2,
.....,'Y'
FROM <old_table>
WHERE processed_in = 'N';

Resources