this is my trigger in sql server. I want this trigger to fire automatically( i.e daily) instead of update...
create trigger trig_name
on tab_name
for update
as
begin
declare #id int,#innn int
declare #dif int
declare #inn int
set #id=(select whateverid from inserted)
set #inn = (select DATEDIFF(DAY,INSERTDT,UPDATEDDT) from tab_name where whateverid=#id)
set #innn =(select DATEDIFF(DAY,INSERTDT,GETDATE()) from tab_name where whateverid=#id)
set #dif = #inn-#innn
update tab_name set due=#dif from tab_name where whateverid= #id
end
Create a new SQL Agent Job, and add a Transact SQL step:
update tab_name
set due = DATEDIFF(DAY, INSERTDT, UPDATEDDT) - DATEDIFF(DAY, INSERTDT, GETDATE())
Obviously, unlike the trigger, you can't update those that have just been updated. So this will update all 'due' fields based on the time it runs.
I would consider create a stored proc and getting the job to run that instead. It easier manager, and less likely to get missed in the future.
Related
I am adding the date to a column in SQL when the 'workstatus' is 'completed', but my problem is, when I open and save the same job again in the software, it runs the trigger and changes the date again to a new value which I don't want.
I want the trigger to run only if the 'workstatus' value is something else than 'completed'.
GO
/****** Object: Trigger [dbo].[TRJCD_JOBREQUEST] Script Date: 06/25/2021 15:49:04 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TRJCD_JOBREQUEST] ON [dbo].[TBL_JOBREQUEST]
AFTER UPDATE,INSERT
AS
if (Update (workstatus))
begin
DECLARE #Jobcompletiondate datetime
DECLARE #workstatus VARCHAR(15)
DECLARE #jobid int
select #workstatus = workstatus from inserted
select #jobid = jobid from inserted
select #Jobcompletiondate = GETDATE()
begin
if #workstatus='Completed'
update TBL_JOBREQUEST set JobCompDate=#Jobcompletiondate where jobid = #jobid
end
end
The following is how you should construct your trigger.
There is no need to assign any values to variables, triggers fire once per batch and always operate on the set of updated rows.
If you update a status to Completed you need to check it's not currently Completed, also if you want to retain the first JobCompDate even if the status is amended afterwards simply use a case expression to only update the column where it's currently NULL.
create or alter trigger [dbo].[TRJCD_JOBREQUEST] on [dbo].[TBL_JOBREQUEST]
after update,insert
as
if ##RowCount=0 return
set nocount on
if Update (workstatus)
begin
update t set
t.JobCompDate=case when t.JobCompDate is null then GetDate() else t.JobCompDate end
from inserted i join TBL_JOBREQUEST t on t.jobid=i.jobid
where i.workstatus='Completed'
and not exists (
select * from deleted d
where d.jobid=i.jobid and d.workstatus=i.workstatus
)
end
Please note that I do not have your data set, so I'm unable to test the trigger, however, based on what you provided in your question, I believe this is the answer you are seeking:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TRJCD_JOBREQUEST] ON [dbo].[TBL_JOBREQUEST]
AFTER UPDATE,INSERT
AS
if (Update (workstatus))
begin
DECLARE #Jobcompletiondate datetime
DECLARE #currentworkstatus VARCHAR(15)
DECLARE #oldworkstatus VARCHAR(15)
DECLARE #jobid int
select #oldworkstatus = workstatus from deleted
select #currentworkstatus = workstatus from inserted
select #jobid = jobid from inserted
select #Jobcompletiondate = GETDATE()
begin
if #currentworkstatus='Completed' and #oldworkstatus <> 'Completed'
update TBL_JOBREQUEST set JobCompDate=#Jobcompletiondate where jobid = #jobid
end
end
You needed to check if the deleted workstatus does not equal Completed and only then should the trigger fire.
When running this command in SQL Server Management Studio
UPDATE LogChange
SET Created = GETDATE()
WHERE id > 6000
something strange happens.. :)
All rows get the same value in the Created column.
How do I "force" SQL Server to recalculate the GetDate for every row?
A cursor solved it
declare #id int;
Declare toLoop cursor for
select Id from LogChange where id > 6000
OPEN toLoop
FETCH NEXT FROM toLoop INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
Update LogChange set Created = GETDATE() where id = #id
FETCH NEXT FROM toLoop INTO #id
END
close toLoop
DEALLOCATE toLoop
I have found the following “getdate()” pattern extremely useful over the years. Especially when updating multiple tables in the same transaction. Having the time values exactly match had more benefit to me then being accurate to the millisecond on individual rows.
declare #dtNow datetime
SET #dtNow = GETDATE()
UPDATE LogChange
SET Created = #dtNow
WHERE id > 6000
I don't think there is a way to do them in a single query because the operations in the same clause of a query are evaluated all-at-once.
If you want to guarantee that separate values are applied to each row, write a query that definitely does that:
declare #dt datetime
set #dt = GETDATE()
;With Diffs as (
select
*,
ROW_NUMBER() OVER (ORDER BY id) * 10 as Offset
from
LogChange
WHERE id > 6000
)
UPDATE Diffs
SET Created = DATEADD(millisecond,Offset,#dt)
We know that the above will generate separate offsets for each row, and that we're definitely adding those offsets to a fixed value. It has often been observed that GETDATE() will return the same value for all rows but I cannot find any specific documentation that guarantees this, which is why I've also introduced a #dt variable so that I know it's only assigned once.
You can create a scalar function like this one:
CREATE FUNCTION dbo.sf_GetDate(#num int)
RETURNS DateTime
AS
BEGIN
RETURN GETDATE()
END
GO
Then use it like this:
UPDATE LogChange
SET Created = dbo.GetDate(Id)
WHERE id > 6000
Instead of trigger i am planning to write a procedure which we can run using job which will work same way as TRIGGER
with these two tables in the same way.
how can i do that?
here are my tables with column names
1.tblcal
ID(int,not null)
UID(varchar(10),null)
Desc(varchar(200),null)
Date(datetime,null)
avbl(varchar(5),null)
2.tblEvent
ID(int,notnull)
UID(varchar(10),null)
Desc(varchar(200),null)
Date(datetime,null)
Down is my trigger on tblEvent..
ALTER TRIGGER [dbo].[trU] ON [dbo].[tblEvent]
FOR INSERT
AS
Declare #CuID char(6),
#CuDesc char(40),
#CuDate datetime
SET NOCOUNT ON
Select #CuID = i.UID , #CuDesc=i.Desc, #CuDate=i.Date From Inserted i
If(#CuDesc !='available')
Begin
Update tblCal set avbl='Out', Desc=#CurDesc where cadate=#CuDate and UID=#CuID
ENd
SET NOCOUNT OFF
I have another problem with Desc column.Desc which are going to be in and out Basically we need to update tblcal differently for different descriptions;in that case I don't think trigger is that reliable;Means for example for 10 Desc we need to update in and for other 10 we need to update out
Actually every thursday on the tblevent data is loaded once its loaded it fired a trigger and will update in tblcal.
but my client is looking for a procedure which we can schedule as a job after the tblevent entry done on Thursday.
How can i do with stored procedure?
Procedure
CREATE PROCEDURE dbo.usp_UpdateEventData
AS
BEGIN
SET NOCOUNT ON;
UPDATE C
SET c.avbl = 'Out'
,c.[Desc] = e.[Desc]
FROM [dbo].tblCal C
INNER JOIN [dbo].[tblEvent] e ON c.[UID] = e.[UID]
AND c.cadate = e.[Date] --<-- check if you only want
WHERE e.[Desc] <> 'available' -- to join on date not datetime
END -- CAST both columns to DATE
Also if you are keeping your Trigger as it is you will need to modify the trigger definition to handle multiple Inserts, You can use the same logic as in this procedure to update your trigger definition.
Trigger Fix
ALTER TRIGGER [dbo].[trU] ON [dbo].[tblEvent]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE C
SET c.avbl = 'Out'
,c.[Desc] = i.[Desc]
FROM [dbo].tblCal C
INNER JOIN inserted i ON c.[UID] = i.[UID]
AND c.cadate = i.[Date]
WHERE i.[Desc] <> 'available'
END
Whenever INSERT is happened in the CUSTOMER table,I need to call the "StoredProcedure1"and
UPDATE is happend in the CUSTOMER table,I need to call the "StoredProcedure2" in the Trigger.
How to determine if insert or update in the trigger from SQL Server 2008.
Some one can please help me how to solve?
Code:
CREATE TRIGGER Notifications ON CUSTOMER
FOR INSERT,UPDATE
AS
BEGIN
DECLARE #recordId varchar(20);
set #recordId= new.Id;
//if trigger is insert at the time I call to SP1
EXEC StoredProcedure1 #recordId
//if trigger is Upadeted at the time I call to SP2
EXEC StoredProcedure2 #recordId
END
Let SQL Server be SQL Server, and have it do the work for you!
Create separate triggers for each change event (insert,update and/or delete).
Put the logic for each into the trigger that needs it.
No need to have to check for the event type.
And don't call a procedure unless it is quick, fast, and can't block others.
The easiest way to solve this problem would be to have two triggers, one for the insert and one for the update.
CREATE TRIGGER InsertNotifications ON CUSTOMER
FOR INSERT
AS
BEGIN
DECLARE #recordId varchar(20);
set #recordId= new.Id;
//if trigger is insert at the time I call to SP1
EXEC StoredProcedure1 #recordId
END
CREATE TRIGGER UpdateNotifications ON CUSTOMER
FOR UPDATE
AS
BEGIN
DECLARE #recordId varchar(20);
set #recordId= new.Id;
//if trigger is Upadeted at the time I call to SP2
EXEC StoredProcedure2 #recordId
END
Try this code for trigger for INSERT, UPDATE and DELETE. This works fine on Microsoft SQL SERVER 2008
if (Select Count(*) From inserted) > 0 and (Select Count(*) From deleted) = 0
begin
print ('Insert...')
end
if (Select Count(*) From inserted) = 0 and (Select Count(*) From deleted) > 0
begin
print ('Delete...')
end
if (Select Count(*) From inserted) > 0 and (Select Count(*) From deleted) > 0
begin
print ('Update...')
end
On an INSERT, the virtual DELETED table will be empty.
create or replace trigger comp
before
insert or delete or update
on student
referencing old as o new as n
for each row
begin
if deleting then
insert into student_backup values
(:o.studid,:o.studentname,:o.address,:o.contact_no,:o.branch,sysdate);
end if;
if inserting then
insert into student_backup values
(:n.studid,:n.studentname,:n.address,:n.contact_no,:n.branch,sysdate);
end if;
if updating then
insert into student_backup values
(:o.studid,:o.studentname,:o.address,:o.contact_no,:o.branch,sysdate);
end if;
end comp;
I have a trigger which deals with some data for logging purposes like so:
CREATE TRIGGER trgDataUpdated
ON tblData FOR UPDATE
AS
BEGIN
INSERT INTO tblLog ( ParentID, OldValue, NewValue, UserID )
SELECT deleted.ParentID, deleted.Value, inserted.Value,
#intUserID -- how can I pass this in?
FROM inserted INNER JOIN deleted ON inserted.ID = deleted.ID
END
How can I pass in the variable #intUserID into the above trigger, as in the following code:
DECLARE #intUserID int
SET #intUserID = 10
UPDATE tblData
SET Value = #x
PS: I know I can't literally pass in #intUserID to the trigger, it was just used for illustration purposes.
I use SET CONTEXT_INFO for this kind of action. That's a 2008+ link, prior link has been retired.
On SQL Server 2005+, you'd have CONTEXT_INFO to read it but otherwise you have to get from context_info column in dbo.sysprocesses.
you can't pass a variable into a trigger.
the only way to get the information in the trigger is to be able to SELECT it based on the INSERTED or DELETED tables or add a column onto the affected table and put the value in that column.
EDIT in the previous question OP posted about this, they said that they didn't want to use CONTEXT_INFO, but here they say it is Ok to use, so here is a CONTEXT_INFO usage example:
in the procedure doing the update
DECLARE #intUserID int
,#CONTEXT_INFO varbinary(128)
SET #intUserID = 10
SET #CONTEXT_INFO =cast('intUserID='+CONVERT(varchar(10),#intUserID)+REPLICATE(' ',128) as varbinary(128))
SET CONTEXT_INFO #CONTEXT_INFO
--do update that will fire the trigger
SET CONTEXT_INFO 0x0
here is the portion of the trigger to retrieve the value:
DECLARE #intUserID int
,#sCONTEXT_INFO varchar(128)
SELECT #sCONTEXT_INFO=CAST(CONTEXT_INFO() AS VARCHAR) FROM master.dbo.SYSPROCESSES WHERE SPID=##SPID
IF LEFT(#sCONTEXT_INFO,9)='intUserID'
BEGIN
SET #intUserID=RIGHT(RTRIM(#sCONTEXT_INFO),LEN(RTRIM(#sCONTEXT_INFO))-10)
END
ELSE
BEGIN
RAISERROR('intUserID was not specified',16,1)
ROLLBACK TRAN
RETURN
END
..use the #intUserID
Old question, but I wonder how come nobody mentioned that temporary tables created before the trigger is invoked are visible in the trigger?
So, this would work:
SELECT 10 intUserID INTO #intUserID
UPDATE tblData
SET Value = #x
The trigger will see the temp table #intUserID and can read the id from there.
I use the sp_set_session_context stored procedure to set the value:
exec sp_set_session_context #key = N'userid', #value = 123
And in my trigger to read the value:
DECLARE #userid int
SELECT #userid = cast(SESSION_CONTEXT(N'userid') as int)
You cant pass variables to triggers. Depending on how users connect to the database you could use SYSTEM_USER to get the current user connected to the database.
You do not pass variables to triggers because you are not able to call triggers directly. They are executed as a result of data being inserted, modified or deleted.