Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute stored procedure from a Trigger after a time delay

I want to call stored procedure from a trigger, how to execute that stored procedure after x minutes? I'm looking for something other than WAITFOR DELAY

thanks

like image 237
Mohammed Rabee Avatar asked Jun 28 '12 07:06

Mohammed Rabee


People also ask

Can a trigger execute a stored procedure?

A: Yes, we can call stored procedure inside the trigger.

Can we schedule a stored procedure execution?

You can schedule a stored procedure to run at a particular time, at an interval, or when a specified event occurs. The administrative task scheduler manages these requests.

Can we use procedure in trigger?

Trigger transition variables can also be used as parameters to a procedure. For SQL procedures you can optionally capture the return status of the procedure by using the GET DIAGNOSTICS statement.


2 Answers

Have an SQL Agent job that runs regularly and pulls stored procedure parameters from a table - the rows should indicate also when their run of the stored procedure should occur, so the SQL Agent job will only pick rows that are due/slightly overdue. It should delete the rows or mark them after calling the stored procedure.

Then, in the trigger, just insert a new row into this same table.

You do not want to be putting anything in a trigger that will affect the execution of the original transaction in any way - you definitely don't want to be causing any delays, or interacting with anything outside of the same database.


E.g., if the stored procedure is

CREATE PROCEDURE DoMagic
    @Name varchar(20),
    @Thing int
AS
  ...

Then we'd create a table:

CREATE TABLE MagicDue (
    MagicID int IDENTITY(1,1) not null, --May not be needed if other columns uniquely identify
    Name varchar(20) not null,
    Thing int not null,
    DoMagicAt datetime not null
)

And the SQL Agent job would do:

WHILE EXISTS(SELECT * from MagicDue where DoMagicAt < CURRENT_TIMESTAMP)
BEGIN
    DECLARE @Name varchar(20)
    DECLARE @Thing int
    DECLARE @MagicID int

    SELECT TOP 1 @Name = Name,@Thing = Thing,@MagicID = MagicID from MagicDue where DoMagicAt < CURRENT_TIMESTAMP

    EXEC DoMagic @Name,@Thing

    DELETE FROM MagicDue where MagicID = @MagicID
END

And the trigger would just have:

CREATE TRIGGER Xyz ON TabY after insert
AS
    /*Do stuff, maybe calculate some values, or just a direct insert?*/
    insert into MagicDue (Name,Thing,DoMagicAt)
    select YName,YThing+1,DATEADD(minute,30,CURRENT_TIMESTAMP) from inserted

If you're running in an edition that doesn't support agent, then you may have to fake it. What I've done in the past is to create a stored procedure that contains the "poor mans agent jobs", something like:

CREATE PROCEDURE DoBackgroundTask
AS

     WHILE 1=1
     BEGIN
         /* Add whatever SQL you would have put in an agent job here */

         WAITFOR DELAY '00:05:00'
     END

Then, create a second stored procedure, this time in the master database, which waits 30 seconds and then calls the first procedure:

CREATE PROCEDURE BootstrapBackgroundTask
AS
    WAITFOR DELAY '00:00:30'
    EXEC YourDB..DoBackgroundTask

And then, mark this procedure as a startup procedure, using sp_procoption:

EXEC sp_procoption N'BootstrapBackgroundTask', 'startup', 'on'

And restart the service - you'll now have a continuously running query.

like image 199
Damien_The_Unbeliever Avatar answered Nov 13 '22 09:11

Damien_The_Unbeliever


I had kind of a similar situation where before I processed the records inserted into the table with the trigger, I wanted to make sure all the relevant related data in relational tables was also there.

My solution was to create a scratch table which was populated by the insert trigger on the first table.

The scratch table had a updated flag, (default set to 0), and an insert get date() date field, and the relevant identifier from the main table.

I then created a scheduled process to loop over the scratch table and perform whatever process I wanted to perform against each record individually, and updating the 'updated flag' as each record was processed.

BUT, here is where I was a wee bit clever, in the loop over process looking for records in the scratch table that had a update flag = 0, I also added the AND clause of AND datediff(mi, Updated_Date, getdate())> 5. So the record would not actually be processed until 5 minutes AFTER it was inserted into the scratch table.

like image 23
Duvvit Avatar answered Nov 13 '22 09:11

Duvvit