Create PL/SQL WatchDog/Daemon in Oracle 11gr2 - oracle

I should create a watchdog that monitor a field in a table every 5 minutes, in Oracle DB. If field has a specific value (a date older than x) an action should be performed.
Is it possible to do it in PL/SQL?
If it's not, I should create a script shell and call it from crontab, or maybe use the Scheduler.

If field has a specific value (a date older than x) an action should be performed.
You could create a TRIGGER. If a new row is inserted such that the date column has a specific value, then you perform some action in the trigger.
Perhaps, you need an AFTER INSERT TRIGGER FOR EACH ROW, since you need to reference the :NEW values.
Here is the link to documentation regarding CREATE TRIGGER.
If you really want to do it as a scheduled job, then you could use DBMS_SCHEDULER. Prior to 10g releases, it was DBMS_JOB.

What you need is to use the DBMS_SCHEDULER package
The DBMS_SCHEDULER package provides a collection of scheduling functions and procedures that are callable from any PL/SQL program. (...)
The Scheduler uses a rich calendaring syntax to enable you to define repeating schedules, such as "every Tuesday and Friday at 4:00 p.m." or "the second Wednesday of every month."

Sorry, maybe I've omitted a detail the field to monitor is for a specific row. I mean if the result of the query:
select up_date from mytab where name_id='test'
is older than 15minutes
then
update mytab set value_col='no' where name_id='test'
But I don't know if it's possible to do it with a trigger. With SCHEDULER I could check at interval time and could be a good work-around.

You may still have a AFTER UPDATE trigger and in that trigger check if the updated row is the one you want to watch. Something like this:
IF :old.name_id = 'test' THEN
-- do something here
END IF;

Related

PL/SQL Trigger on INSTERT OR UPDATE : ":NEW" IS NULL => ambiguous?

I'm quite a newbie in PL/SQL and I'm trying to do quite complex data integrity checks via triggers.
I've already understood how to avoid problems when calling a table inside a trigger that is used on the same table (via a temporary external table) but now I'm facing a really mind-blowing problem : I thought that ":NEW" was referencing the value in my table AFTER an update but things don't look that simple... It is the new value SET by the update or insert... which looks to be NULL if nothing has been specified, even if the corresponding field value is NOT NULL after the update... wich is driving me crazy.
My trigger is set when inserting or updating several variables :
CREATE OR REPLACE TRIGGER TRG_INS_UP_INSTRUMENT_EVENT
AFTER INSERT OR UPDATE OF EVENT_ID, DATE_BEGIN,DATE_END,INSTR_ID,TYPE_EVENT_ID ON AIS_INSTRUMENT_EVENT
But now... If there already is a line with non-null fields and I do an
UPDATE AIS_INSTRUMENT_EVENT SET INSTR_ID='642' WHERE EVENT_ID='6479'
I actually get a ":NEW.DATE_BEGIN" which is NULL... event thought nor the older or newer values are NULL (because I just didn't update it).
How can I distinguish - in my trigger - the case when the DATE_BEGIN is updated and SET voluntary to NULL from the case in which nothing has been specified (and this field must thus remain the same but not necessarily NULL...). I have to many possible combination to check one by one...
Thanks in advance for your help!
What you are saying is not true. :new contains the full row regardless whether the column is referenced in the UPDATE statement:
CREATE TABLE test (test INTEGER, last_changed DATE);
CREATE OR REPLACE TRIGGER TRG_INS_UP_TEST
AFTER INSERT OR UPDATE OF test, last_changed ON test
FOR EACH ROW
BEGIN
dbms_output.put_line('LAST CHANGED IS ' || :new.last_changed);
END;
INSERT INTO test (test, last_changed) VALUES (1, SYSDATE);
COMMIT;
UPDATE test SET test = test + 1;
DBMS Output:
LAST CHANGED IS 01.09.17
To achieve what you want the mechanism works slightly different. You have to look at two different use cases:
1.) You want the trigger not to fire unless a certain column is mentioned. This use cases is by the reference in the trigger declaration (INSERT OR UDATE OF "column_name"). If the INSERT/UPDATE statement only affects columns that are not mentioned the trigger will not fire.
2.) You want the trigger not to fire unless a certain row is modified. So you want the trigger to only if fire is a value has actually changed. This is done by the WHEN restriction of the trigger. It is usually used in conjunction with DECODE, like so:
CREATE OR REPLACE TRIGGER TRG_INS_UP_TEST
AFTER INSERT OR UPDATE OF test, last_changed ON test
FOR EACH ROW
WHEN (DECODE(new.test,old.test,0,1)=1 OR DECODE(new. last_changed,old. last_changed,0,1)=1)
BEGIN
...
END;
So to answer your original question: If you want to the trigger too only fire in cases where the column DATE_BEGIN is set to NULL you will have to declare your trigger using both approaches
CREATE OR REPLACE TRIGGER TRG_INS_UP_INSTRUMENT_EVENT
AFTER INSERT OR UPDATE OF DATE_BEGIN ON AIS_INSTRUMENT_EVENT
FOR EACH ROW
WHEN (DECODE(new.DATE_BEGIN,old. DATE_BEGIN,0,1)=1 AND new.DATE_BEGIN IS NULL)
The limitation to certain columns ("INSERT OR UPDATE OF DATE_BEGIN") is not strictly necessary but it is good practice since it improves performance since it excludes the trigger from firing at all.
Sorry I think I made a to quick conclusion... The bug was mine. I've tested on a "Toy" table and, indeed, the :NEW was not null, even when not set by the UPDATE. I found the bug in the meantime. All this is too new to me ;-).
Sorry for disturbing.

Oracle Update multiple columns with same value

Lets suppose I created one table--
Create table t1 (aa varchar2(5),bb varchar2(5),cc varchar2(5));
Inserted values in it--
insert into T1 values ('a','b','c');
commit;
Now in one scenario, if i wanted to update all columns with same value then I am doing by this way--
UPDATE T1 SET AA='x',BB='x',CC='x';
Is there any another way by which this update task can be accomplished considering in real time there may be quite large number of columns and all has to be updated with same value in one go?
I am using Oracle 11.2.0.1.0 - 64bit Production
Note: Usually there are very less any scenarios where same values are being updated for all columns. But for example consider a school database and a good student scores 10/10 marks in all subjects. :-)
Thanks.
There is no way to do it in pure SQL. You must list down all the columns explicitly in the UPDATE statement.
And, believe me it is not a difficult task using a good text editor. Using the metadata you could get the list of column names in few seconds, all you need to do is prepare the SQL statement as per the syntax.
If you really want to do it dynamically, then you need to do it in PL/SQL and (ab)use EXECUTE IMMEDIATE. I would personally not suggest it unless you are just doing it for learning purpose.
You could try this:
UPDATE T1 SET AA='x',BB=AA,CC=AA;

Make column readonly on specific condition oracle

I am trying to create some kind of trigger to prevent a row being edited if it is after today's date (will use SYSDATE to get that).
I am unsure about how to do this as I am new to PL/SQL and would think perhaps some kind of package that gets the date using a cursor then uses a function to return a boolean to a procedure which then somehow stops the DML statement from firing?
Thanks in advance
Obviously you need a date column as your target. Truncating SYSDATE will give you midnight. Consequently if a truncated SYSDATE is greater than another date it must be at least the next day.
Raising an application error will cause the update to fail. Note that if you're updating multiple rows a single failure will rollback all the changes.
create or replace trigger your_trg
before update on your_table
for each row
begin
if trunc(sysdate) > :old.whatever_date then
raise_application_error(-20000, 'It is too late to change this record');
end if;
end;
the solution you are looking for is VPD Column masking.
mainly used for security purposes, VPD enables you to define row/colums level rules for data access and display.

How to fire a trigger after finishing data insert in a table

I have a table A into which I am inserting data. Then some calculation is being done updating the same table A.
I want to fire a trigger, which calls a Procedure A after the completion of data insertion ( after insert and update ).
How do I do this?
Is there any other way to do it automatically... Or do I have to run Procedure A manualy after the completion of data insertion in table A.
More simply, I would like to know how to fire a trigger after inserting a few rows and a commit, i.e. not for each row.
You can define your trigger to be fired for each row or for each statement (FOR EACH ROW option).
If I understood you right, you would like to fire the trigger after a bunch of statements? Don't think you can. Even if you can, I would rather not do it. They scatter your program flow / logic and make it harder to understand later how your software works.
Regards
If I understand your question correctly, you want the trigger to fire after you completed your transaction consisting of several insert/update statements? If that is the case, I think you should consider calling your Procedure A in your program flow right after the insert/update operations are done.
In other words: A trigger would only be useful, if it should be called for each row or for each statement.
Add one column to your table: e.g "FINAL_ACTION". Leave this column untouched untill your anticipated final action. Then have your trigger get fired only with this clause:
REFERENCING NEW AS NEWREC OLD AS OLDREC
FOR EACH ROW
WHEN (NEWREC.FINAL_ACTION <> OLDREC.FINAL_ACTION)
DECLARE
--YOUR DECLARATIONS
BEGIN
--DO SOMETHING
END;

how to execute procedure with timer in oracle

I need to clear all records in a table if the data exist for one hour.
to know the start time, I have a column "StartTime" with "date" data type.
I think I need a timer to do this,
how can I do this in oracle ?
Depending on what your exact requirements are, I would probably look at using a view to only show the valid rows when queried. This would make it seem like only the last one hour of records were available. This would also mean that you don't need to remove rows exactly an hour after they are created.
Then to remove the rows, I would look at using DBMS_JOB or DBMS_SCHEDULER to remove the rows as has been suggested in some of the other answers.
Remember that just because your requirement is to clear the rows from the table after an hour, you probably really only need to remove the ability to query on them, which you could do with a view.
For what version of Oracle?
For version v7.3.4 to 9i, use DBMS_JOB to schedule a task. 10g+, you want to use DBMS_SCHEDULER. It's not clear to me how often you want/need this to run...
You can create scheduled Jobs in Oracle 10G and above using the DBMS_SCHEDULER
If you are really fastidious, you can schedule this job - which calls your procedure - to run every 1 minute so that the data is cleared out as soon as the 60th minute expires.
Refer this link for an example of how to setup / schedule a job via scripts in Oracle 10G
The requirements are not Quite clear.
Is this a job/program that you have to run once and will delete records that existed for more than an hour? If that is the case, you can use..
delete from <table_name>
where StartTime < (sysdate-1/24);
commit;
If you need to purge records constantly, you'll need to schedule this as a job . The frequency will depend on how often you want the records to be deleted.
What is the business case you are trying to solve?

Resources