I have a trigger that calls a stored procedure when activated, passing :NEW values as a parameter. I have about 40 tables that use the same trigger, and I would like to use the same code for each trigger. Therefore, I am trying to pass all columns of a new row. My code is below and shows what I am attempting to do (however, the problem is that :NEW.* is not a valid expression):
CREATE OR REPLACE TRIGGER "TRIG_TEST_TRIGGER"
AFTER INSERT OR DELETE OR UPDATE ON TRIG_TEST
FOR EACH ROW
DECLARE
BEGIN
MY_STORED_PROC('Trigger Activated: ' || :NEW.*);
END;
Most likely, you can't.
You could write a procedure that uses dynamic SQL to generate the appropriate trigger code for each table. Of course, that would require that you re-run the procedure to re-create the trigger every time the table changes.
I'm a bit hard-pressed, though, to imagine what my_stored_proc might be doing that it would make sense to pass it a string representing every column from 1 of 40 tables with, presumably, 40 different sets of columns. If you're writing to a log table, if you want the data from every column, that generally implies that you want to be able to see the evolution of a particular row over time. But that is extremely hard to do if your log table just has strings in all sorts of different formats from many different tables since you'd constantly have to do things like parsing the string that you logged.
Related
I am new to Oracle and would like to know how to make this trigger work please. I can do each trigger separately but I need them all in the same one if that makes sense.
create trigger ID_trigger
before insert on crime, evidence, offence, officer
for each row
begin
select crime_seq.nextval into :new.crime_id from dual
and officer_seq.nextval into :new.officer_id from dual
and evidence_seq.nextval into :new.evidence_id from dual
and offence_seq.nextval into :new.offence_id from dual;
end;
I initially had a single trigger for each table. However when submitting data into my form the triggers seemed to have overwritten the previous one
" I initially had a singler trigger for each table, however when
sumbitting data into my form the triggers seemed to have overwritten
the previous one"
Let's guess: you called all four triggers id_trigger. Each subsequent CREATE OR REPLACE TRIGGER call would overwrite the first one. Unless you used CREATE TRIGGER as you do here, in which case each subsequent call would fail, Either way, only one table would have a trigger.
You see, even though they belong to a table, triggers are separate database objects. So, like indexes or constraints, their names must be unique within the schema.
The solution is simple: give each trigger a different name, say by including the table name.
Long time user, first time "asker".
I am attempt to construct an Oracle procedure and/or trigger that will compare two tables with the MINUS operation and then insert any resulting rows into another table. I understand how to do the query in standard SQL, but I am having trouble coming up with an efficient way to do this using PL/SQL.
Admittedly, I am very new to Oracle and pretty green with SQL in general. This may be a silly way to go about accomplishing my goal, so allow me to explain what I am attempting to do.
I need to create some sort of alert that will be triggered when the V_$PARAMETER view is changed. Apparently triggers can not respond to changes to view but, instead, can only replace actions on views...which I do not wish to do. So, what I did was create a table that to mirror that view to essentially save it as a "snapshot".
create table mirror_v_$parameter as select * from v_$parameter;
Then, I attempted to make a procedure that would minus these two so that, whenever a change is made to v_$parameter, it will return the difference between the snapshot, mirror_v_$parameter. I trying to create a cursor with the command:
select * from v_$parameter minus select * from mirror_v_$parameter;
to be used inside a procedure, so that it could be used to fetch any returned rows and insert them into another table called alerts_v_$parameter. The intent being that, when something is added to the "alert" table, a trigger can be used to somehow (haven't gotten this far yet) notify my team that there has been a change to the v_$parameter table, and that they can refer to alerts_v_$parameter to see what has been change. I would use some kind of script to run this procedure at a regular interval. And maybe, some day down the line when I understand all this better, manipulate what goes into the alerts_v_$parameter table so that it provides better information such as specifically what column was changed, what was its previous value, etc.
Any advice or pointers?
Thank you for taking the time to read this. Any thoughts will be very appreciated.
I would create a table based on the exact structure of v_$parameter with an additional timestamp column for "last_update", and periodically (via DBMS_Scheduler) merge into it any changes from the real v_$parameter table and capture the timestamp of any detected change.
You might also populate a history table at the same time, either using triggers on update of your table or with SQL.
PL/SQL is unlikely to be required, except as a procedural wrapper to the SQL code.
Examples of Merge are in the documentation here: http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_9016.htm#SQLRF01606
Friend, I have question about cascade trigger.
I have 2 tables, table data that has 3 attributes (id_data, sum, and id_tool), and table tool that has 3 attributes (id_tool, name, sum_total). table data and tool are joined using id_tool.
I want create trigger for update info sum_total. So , if I inserting on table data, sum_total on table tool where tool.id_tool = data.id_tool will updating too.
I create this trigger, but error ora-04090.
create or replace trigger aft_ins_tool
after insert on data
for each row
declare
v_stok number;
v_jum number;
begin
select sum into v_jum
from data
where id_data= :new.id_data;
select sum_total into v_stok
from tool
where id_tool=
(select id_tool
from data
where id_data= :new.id_data);
if inserting then
v_stok := v_stok + v_jum;
update tool
set sum_total=v_stok
where id_tool=
(select id_tool
from data
where id_data= :new.id_data);
end if;
end;
/
please give me opinion.
Thanks.
The ora-04090 indicates that you already have an AFTER INSERT ... FOR EACH ROW trigger on that table. Oracle doesn't like that, because the order in which the triggers fire is unpredictable, which may lead to unpredictable results, and Oracle really doesn't like those.
So, your first step is to merge the two sets of code into a single trigger. Then the real fun begins.
Presumably there is only one row in data matching the current value of id_data (if not your data model is rally messed up and there's no hope for your situation). Anyway, that means the current row already gives you access to the values of :new.sum and :new.id_tool. So you don't need those queries on the data table: removing those selects will remove the possibility of "mutating table" errors.
As a general observation, maintaining aggregate or summary tables like this is generally a bad idea. Usually it is better just to query the information when it is needed. If you really have huge volumes of data then you should use a materialized view to maintain the summary, rather than hand-rolling something.
I have a shell script which calls a stored procedure which is inserting data in the Oracle DB. I takes 4-5 hours to run the procedure. I want to add a log in the stored procedure which would display the no. of records inserted in the DB after a particular interval of time (say in evrey 20 minutes).
Is there a way to add logs in the stored procedure?
It depends how you're doing it. If you're just doing a straight insert only insert into ... select ... then no, there's no way. However, if you have some sort of looping in there you can use the dbms_application_info package to record your actions in the V$SESSION view.
I normally do something like this:
dbms_application_info.set_module('Updating Blah','Total: ' || <index var>);
The first parameter is the module_name, which you can view on the module column and the second parameter is action_name, which is the action column in V$SESSION.
Alternatively you can always insert or update a smaller table which tracks what you're doing and can therefore by asynchronously queried.
I need to write a sproc which performs some INSERTs on a table, and compile a list of "statuses" for each row based on how well the INSERT went. Each row will be inserted within a loop, the loop iterates over a cursor that supplies some values for the INSERT statement. What I need to return is a resultset which looks like this:
FIELDS_FROM_ROW_BEING_INSERTED.., STATUS VARCHAR2
The STATUS is determined by how the INSERT went. For instance, if the INSERT caused a DUP_VAL_ON_INDEX exception indicating there was a duplicate row, I'd set the STATUS to "Dupe". If all went well, I'd set it to "SUCCESS" and proceed to the next row.
By the end of it all, I'd have a resultset of N rows, where N is the number of insert statements performed and each row contains some identifying info for the row being inserted, along with the "STATUS" of the insertion
Since there is no table in my DB to store the values I'd like to pass back to the user, I'm wondering how I can return the info back? Temporary table? Seems in Oracle temporary tables are "global", not sure I would want a global table, are there any temporary tables that get dropped after a session is done?
If you are using Oracle 10gR2 or later then you should check out DML error logging. This basically does what you want to achieve, that is, it allows us to execute all the DML in a batch process by recording any errors and pressing on with the statements.
The principle is that we create an ERROR LOG table for each table we need to work with, using a PL/SQL built-in package DBMS_ERRLOG. Find out more. There is a simple extension to the DML syntax to log messages to the error log table. See an example here. This approach doesn't create any more objects than your proposal, and has the merit of using some standard Oracle functionality.
When working with bulk processing (that is, when using the FORALL syntax) we can trap exceptions using the built-in SQL%BULK_EXCEPTIONS collection. Check it out. It is possible to combine Bulk Exceptions with DML Error Logging but that may create problems in 11g. Find out more.
"Global" in the case of temporary tables just means they are permanent, it's the data which is temporary.
I would define a record type that matches your cursor, plus the status field. Then define a table of that type.
TYPE t_record IS
(
field_1,
...
field_n,
status VARCHAR2(30)
);
TYPE t_table IS TABLE OF t_record;
FUNCTION insert_records
(
p_rows_to_insert IN SYS_REFCURSOR
)
RETURN t_table;
Even better would be to also define the inputs as a table type instead of a cursor.