Replacing trigger with a procedure - sql

I have a table t1 with fields id, name and date. When I update the date to a certain value I want to move that entry to another table t2 (Remove it in t1 and insert it into t2). I explicitly want to do this on an UPDATE operation and not a DELETE operation.
I tried doing it with a AFTER UPDATE ON t1 trigger, however, I found out that I can't delete an entry from t1 in a trigger for t1.
Does that mean I need to do this with a procedure? If yes, how do I make a procedure run everytime I make an UPDATE of the date field to a certain value in t1?

Create a view for table t1 and then on the view create a INSTEAD OF TRIGGER:
create view v1 as select * from t1;
CREATE OR REPLACE TRIGGER IO_V1
INSTEAD OF UPDATE ON V1
FOR EACH ROW
BEGIN
INSERT INTO t2 ...;
END;
Of course, you can encapsulate all in a procedure and run this procedure rather than UPDATE statement directly.

Related

how to see properties available for access with NEW plpgsql

Say I have a PLPGSQL function
CREATE OR REPLACE FUNCTION function_name
RETURNS TRIGGER AS ...
BEGIN
PERFORM
1
FROM
table1 t1
JOIN
table2 t2 USING( column_name )
WHERE
t1.column_name = NEW.column_name;
RETURN NEW;
END;
DROP TRIGGER IF EXISTS trigger_name
ON table1;
CREATE TRIGGER trigger_name
BEFORE INSERT ON table1
FOR EACH ROW EXECUTE PROCEDURE function_name;
I noticed that only some columns in table1 and table2 are accessible with NEW.column_name. How can I see the full list of columns I can access with NEW?
Additionally, if there is a column in table1 or table2 that I cannot access with NEW, how can I make it accessible to NEW?
Look at this line:
BEFORE INSERT ON table1
This tells you that the trigger is executed before an INSERT ON table1. This means that you will have NEW.column_name for any column of table1, while table2 is unchanged by the trigger and makes no sense to use OLD or NEW on it, hence it illegal. So, to be precise: NEW works for table1 columns and does not work on table2 columns.

Values of the inserted row in a Trigger Oracle

I want a trigger that updates the value of a column, but I just want to update a small set of rows that depends of the values of the inserted row.
My trigger is:
CREATE OR REPLACE TRIGGER example
AFTER INSERT ON table1
FOR EACH ROW
BEGIN
UPDATE table1 t
SET column2 = 3
WHERE t.column1 = :new.column1;
END;
/
But as I using FOR EACH ROW I have a problem when I try it, I get the mutating table runtime error.
Other option is not to set the FOR EACH ROW, but if I do this, I dont know the inserted "column1" for comparing (or I dont know how to known it).
What can I do for UPDATING a set of rows that depends of the last inserted row?
I am using Oracle 9.
You should avoid the DML statements on the same table as defined in a trigger. Use before DML to change values of the current table.
create or replace trigger example
before insert on table1
for each row
begin
:new.column2 := 3;
end;
/
You can modify the same table with pragma autonomous_transaction:
create or replace trigger example
after insert on table1 for each row
declare
procedure setValues(key number) is
pragma autonomous_transaction;
begin
update table1 t
set column2 = 3
where t.column1 = key
;
end setValues;
begin
setValues(:new.column1);
end;
/
But I suggest you follow #GordonLinoff answere to your question - it's a bad idea to modify the same table in the trigger body.
See also here
If you need to update multiple rows in table1 when you are updating one row, then you would seem to have a problem with the data model.
This need suggests that you need a separate table with one row per column1. You can then fetch the value in that table using join. The trigger will then be updating another table, so there will be no mutation problem.
`create table A
(
a INTEGER,
b CHAR(10)
);
create table B
(
b CHAR (10),
d INTEGER
);
create trigger trig1
AFTER INSERT ON A
REFERENCING NEW AS newROW
FOR EACH ROW
when(newROW.a<=10)
BEGIN
INSERT into B values(:newROW.b,:newROW.a);
END trig1;
insert into A values(11,'Gananjay');
insert into A values(5,'Hritik');
select * from A;
select * from B;`

Using a table to store deleted rows

In my Oracle DB I've a table called tableA where an application write data. My program reads from tableA and when I've processed the data I delete them. The question is I want to keep a log of every data I've processed and I can't keep them in tableA because I've no control over application A and it might not work if I keep my processed data on that table, so I've created a table identical to tableA called tableB and I've put this trigger on tableA:
create or replace
trigger tableA_delete_trigger
BEFORE DELETE ON tableA
FOR EACH ROW
BEGIN
INSERT INTO tableB
( a,
b,
c,
)
VALUES
( :old.a,
:old.b,
:old.c,
sysdate);
END;
This system work quite well, the real problem is when I need to alter something in tableA I have to replicate by hand the same modification in tableB and if I add/remove coloumn I have to update the trigger.
Is there a better way to do this?
An alternative approach might be to rename TableA and create a view named TableA for application A to use. You would then logically delete rows by whatever means seem appropriate and only expose in the view the rows that are not deleted.
You would still need to modify the view if the table structure changes, but at least you won't need to worry about the trigger.
What about an alter trigger:
CREATE OR REPLACE TRIGGER ddl_trigger AFTER ALTER ON schema
DECLARE
cmd VARCHAR2(32000);
BEGIN
SELECT
upper(sql_text)
INTO
cmd
FROM
v$open_cursor
WHERE
upper(sql_text) LIKE 'ALTER TABLE TABLEA%' ;
SELECT
REPLACE(cmd, 'TABLEA', 'TABLEB')
INTO
cmd
FROM
dual;
EXECUTE IMMEDIATE cmd;
END;
Not sure that will work because of the recursion.

sql server triggers

Hello
I need help for creating trigger which would insert id into table1 when table2 filled with row
I suggest you start here
Trigger should be created in the Table 2 insert
create trigger *< trigger_name >* on
*< table_name >* for INSERT AS
//insert statement for table1
You don't need a trigger for that. Use a regular stored procedure.
When you define a trigger FOR INSERT, you have access to the inserted logical table. You can use it to retrieve the id of the inserted row, and store it in another table.
Something like:
CREATE TRIGGER trig
ON table2
FOR INSERT
AS
INSERT INTO table1 (id)
SELECT ins.id FROM inserted ins
GO

Is it possible to move a record from one table to another using a single SQL statement?

I need a query to move a record from one table to another without using multiple statements?
No, you cannot move records in one SQL statement. You have to use an INSERT followed by a DELETE statement. You should wrap these statements into a transaction, to make sure that the copy operation remains atomic.
START TRANSACTION;
INSERT INTO
new_table
SELECT
*
FROM
old_table
WHERE
some_field = 'your_criteria';
DELETE FROM old_table WHERE some_field = 'your_criteria';
COMMIT;
If you really want to do this in a single SQL statement, one way to accomplish this would be to create an "after delete" trigger on the source table that inserts the row into the target table. This way you can move the row from the source table to the target table simply by deleting it from the source table. Of course this will only work if you want to insert into target table for every delete on the source table.
DELIMITER $$
DROP TRIGGER IF EXISTS TR_A_DEL_SOURCE_TABLE $$
CREATE TRIGGER TR_A_DEL_SOURCE_TABLE AFTER DELETE ON SOURCE_TABLE FOR EACH ROW BEGIN
INSERT IGNORE INTO TARGET_TABLE(id,val1,val2) VALUES(old.id,old.va1,old.val2);
END $$
DELIMITER ;
So to move the row with id 42 from source table to target table:
delete from source_table where id = 42;
No - you might be able to do the INSERT in one nested statement, but you still need to remove the record.
There is no way to make it a single query, but if you HAVE to do it in a single query within an application you can create a Stored Procedure to do it for you.
DELIMITER $$
DROP PROCEDURE IF EXISTS `copydelete` $$
CREATE PROCEDURE `copydelete` (id INT)
BEGIN
INSERT INTO New_Table SELECT * from Old_Table WHERE Old_Table.IdField=id;
DELETE FROM Old_Table where IdField=id;
END $$
DELIMITER ;
Then you're new query is just
CALL copydelete(4);
Which will delete WHERE IdField=4;
Please note that the time delay between insert-select and delete can cause you to delete to much.
For a safe route you could use an update field:
update old_table set move_flag=1 where your_criteria
insert into ... select from ... where move_flag = 1
delete from old_table where move_flag=1
Or use a transaction which locks the old_table so no data can be added between insert... select and delete.