I'm creating a table and setting up a trigger to insert/update field_3 using field_1 and 2. See my example below.
ID | Start_Date | End_Date | Period |
1 3/10/17 3/20/17 10
2 2/05/17 3/10/17 5
Trigger
ALTER TRIGGER [dbo].[tr_insert_Period]
ON [dbo].[MEDICAL_EXCUSE]
for update
AS
BEGIN
SET NOCOUNT ON;
declare #Start_Date Datetime
declare #End_Date Datetime
declare #Period Float
set #Start_Date = ( select me_start_date from inserted)
set #End_Date = ( select ME_End_Date from inserted)
set #Period = ( select Period from inserted)
update MEDICAL_EXCUSE
set #Period = DATEDIFF(day,#Start_Date , #End_Date)
END
it won't trigger just work if you execute Manually.
Any help Much Appreciated.
Don't use a trigger. Just use a computed column. I think this is what you want:
alter table KOPS_MEDICAL_EXCUSE
add period as (DATEDIFF(day, me_start_date, me_end_date));
Note that datediff() returns an integer, so there is no reason to declare anything as a float.
With a computed field, the value is usually calculated when you query the table. You can materialize the value if you want using the PERSISTED keyword.
Several issues I see with your trigger. First you can never assume only one record is going to be updated ever. Enterprise databases (typically the only kind complex enough to need triggers) often are accessed outside the user application and a large insert might appear.
Next, you want to get the records from an insert, so it needs to be an insert trigger. And finally you are updating the value of the variable #period instead of the field Period in your table.
Try this:
ALTER TRIGGER [dbo].[tr_insert_Period]
ON [dbo].[MEDICAL_EXCUSE]
for insert
AS
BEGIN
UPDATE ME
SET Period = DATEDIFF(day,i.Start_Date , i.End_Date)
FROM MEDICAL_EXCUSE ME
JOIN Inserted i on i.id = me.id
END
You might also want this to work for updates as well, so then make it:
FOR Insert, update
Related
I have a master table , say mast_tbl, where every change on this table goes as a different entry into hst_mast_tbl. There is col in both the tables which talks about status change. Now if I want to track the number of records for which only the status has changed in the past 10 days, how can I achieve it in a simplest way?
To use lag function, sometimes there might be more column getting updated in master table so more than 1 record in my history table. So how can I achieve the same.
Thanks in advance.
Have you tried using triggers? If you are using SQL Server this might help you.
You can utilize the trigger by using FOR UPDATE when certain column is edited on your table you could say
CREATE TRIGGER [dbo].[trg_update] ON [dbo].[tbl_trans]
WITH EXECUTE AS CALLER
FOR UPDATE
AS
BEGIN
DECLARE #transaction VARCHAR(15)
DECLARE #action VARCHAR(10)
DECLARE #ref_id INT
DECLARE #trail_date DATETIME
SELECT
#transaction = '<your table here>',
#action = 'Update',
#ref_id = i.trans_id,
#trail_date = GETDATE()
FROM INSERTED i
INSERT INTO tbl_auditTrail (audit_trans,action,ref_id,trail_date)
VALUES (#transaction,#action,#ref_id,#trail_date)
END
GO
something like this.
I'm working on a small database in SQL Server 2008 to track employee changes. I'm having trouble with an Insert Trigger at the moment. What I want to happen, is that when a "Record" is inserted into the Record table, it finds the previous open record (i.e. one without an end date) for that Employee (EmpID), if there is one, and updates it with an EndDate - which will be calculated as the day before the inserted StartDate. Here is what I have tried, to no success:
CREATE TRIGGER [dbo].[trgInsertRecord] ON [dbo].[Record]
FOR INSERT
AS
declare #EmpID int;
declare #StartDate date;
declare #EndDate date;
select #EmpID=i.EmpID from inserted i;
select #StartDate=i.RealStart from inserted i;
set #EndDate=DATEADD(DAY,-1,#StartDate)
UPDATE Record
SET RealEnd=#EndDate
WHERE EmpID=#EmpID AND RealEnd=NULL;
Can somebody please help me understand my mistake?
Assuming only a single row exists for a given EmpID and NULL RealEnd, you can join to the inserted table for the update operation like the example below. The best practice is to code triggers as to handle multiple rows updated by a single statement so you should avoid local scalar subqueries in triggers.
CREATE TRIGGER [dbo].[trgInsertRecord] ON [dbo].[Record]
FOR INSERT
AS
UPDATE r
SET RealEnd = DATEADD(DAY, -1, i.RealStart)
FROM Record AS r
JOIN inserted AS i ON
r.EmpID=i.EmpID
AND RealEnd IS NULL;
I have an SQL trigger as below
GO
create trigger ExpDateCheckCard
On Card
FOR Insert, Update
As
Declare #expDate as DateTime
Select #expDate = inserted.ExpirationDate from inserted
if (YEAR(#expDate) < 1971 )
BEGIN
UPdate Card set ExpirationDate = '1900-01-01 00:00:00' FROM Card, inserted
where inserted.RecordID = Card.RecordID
END
If i am right as per the trigger for every record inserted/updated when trigger runs it will check for the YEAR In the ExpirationDate column of that record and if the value is less than 1971 then it will update it with the date in the update query.
The weird thing is it is not working as expected.
The if condition does not seem to work.
Is anything wrong with this particular trigger.
YES - there's definitely something fundamentally wrong with the way you wrote this trigger.
SQL Server (assuming that's what you're using) will fire the trigger not once per row (as many folks including yourself) seem to think - the trigger is fired once per batch which might update or insert 10, 20, 50 rows at once.
Therefore, the Inserted pseudo table inside the trigger can (and will!) contain multiple rows - and in that case - what exactly does your statement here select?
Select #expDate = inserted.ExpirationDate from inserted
Either you'll just get one randon row (out of 50) and handle that (and ignore all 49 other rows), or you'll get an error....
You need to write your triggers with that in mind - you MUST always assume that Inserted (and Deleted) will contain multiple rows!
So you need to change your trigger to be something like:
CREATE TRIGGER ExpDateCheckCard
ON dbo.Card
FOR Insert, Update
AS
UPDATE dbo.Card
SET ExpirationDate = '1900-01-01 00:00:00'
FROM dbo.Card c
INNER JOIN inserted i ON i.RecordID = c.RecordID
WHERE YEAR(i.ExpirationDate) < 1971
(I've also changed your old-style JOIN syntax (comma-separated list of tables) to the ANSI standard that's been in place since 1992 - please do not use the comma-separated list of tables! Use proper ANSI JOINs. See this blog post for more background info: Bad habits to kick : using old-style JOINs)
try with this :
create trigger ExpDateCheckCard
On Card
FOR Insert, Update
As
BEGIN
Declare #expDate as DateTime
Select #expDate = inserted.ExpirationDate from inserted
if (YEAR(#expDate) < 1971 ) Begin
UPdate Card set ExpirationDate = '1900-01-01 00:00:00' FROM Card, inserted
where inserted.RecordID = Card.RecordID
end
END
You have to try this code
CREATE TRIGGER ExpDateCheckCard AFTER INSERT,Update ON Card
FOR EACH ROW
Declare #expDate as DateTime
Select #expDate = inserted.ExpirationDate from inserted
if (YEAR(#expDate) < 1971 )
BEGIN
update Card
set ExpirationDate = '1900-01-01 00:00:00' FROM Card, inserted
where inserted.RecordID = Card.RecordID
END
I want a SQL trigger to fire when a table is updated. I have a table called SimActivation and a stored procedure called spUpdateMnpDate that takes two parameters, msisdn(varchar) and date(datetime).
When the SimActivation table is updated I want to call the procedure with the new values.
Here is what I got at the moment, after changing it back and forth:
USE StoreSale;
GO
CREATE TRIGGER dbo.tSyncMnpDate
ON [dbo].[SimActivation]
AFTER UPDATE
AS
DECLARE #date datetime
DECLARE #msisdn varchar(10)
SET #date = (select ProcessDate from inserted)
SET #msisdn = (select Msisdn from inserted)
EXEC [spUpdateMnpDate]
#msisdn, #date;
GO
Can anyone point me in the right direction?
Thanks :)
The problem you have is that a trigger will fire when one or more rows have been updated. At the moment you are assuming your trigger will fire for each row, which is not the case in SQL Server.
If the stored procedure you are trying to call is fairly simple I'd pull the code out of there and in to the trigger. But remember you are working with sets of changed rows (even if the change is to only one row) so you have to write your SQL accordingly.
EDIT: I assume your procedure is updating a date where the PK is equal to #msisdn, if so you can do this in your trigger:
UPDATE Your_Table
SET Your_Table.ProcessDate = inserted.ProcessDate
FROM Your_table INNER JOIN inerted ON Your_Table.Msisdn = inserted.Msisdn
Joining the tables ensures it will work for one or many updated rows.
Is there a special way to declare a DateCreated column in a MS Sql Server table so that it will automatically fill it with the appropriate time-stamp when created?
Or.. do I have to provide the datetime to it when I do the query, manually?
Default values suffer from two major drawbacks.
if the insert statement specifies a value for the column, the default isn't used.
the column can be updated any time.
These mean that you can't be certain that the values haven't been modified outside of your control.
If you want true data integrity (so that you're sure the date in the row is the creation date), you need to use triggers.
An insert trigger to set the column to the current date and an update trigger to prevent changes to that column (or, more precisely, set it to its current value) are the way to implement a DateCreated column.
An insert and update trigger to set the column to the current date is the way to implement a DateModified column.
(edit from user Gabriel - here's my attempt to implement this as described - i'm not 100% sure it's correct but I'm hoping the OP reviews it...):
CREATE TRIGGER [dbo].[tr_Affiliate_IU]
ON [dbo].[Affiliate]
AFTER INSERT, UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Get the current date.
DECLARE #getDate DATETIME = GETDATE()
-- Set the initial values of date_created and date_modified.
UPDATE
dbo.Affiliate
SET
date_created = #getDate
FROM
dbo.Affiliate A
INNER JOIN INSERTED I ON A.id = I.id
LEFT OUTER JOIN DELETED D ON I.id = D.id
WHERE
D.id IS NULL
-- Ensure the value of date_created does never changes.
-- Update the value of date_modified to the current date.
UPDATE
dbo.Affiliate
SET
date_created = D.date_created
,date_modified = #getDate
FROM
dbo.Affiliate A
INNER JOIN INSERTED I ON A.id = I.id
INNER JOIN DELETED D ON I.id = D.id
END
You can set the default value of the column to "getdate()"
We have DEFAULT on CreatedDate and don't enforce with Triggers
There are times when we want to set the date explicitly - e.g. if we import data from some other source.
There is a risk that Application Bug could mess with the CreateDate, or a disgruntled DBA for that matter (we don't have non-DBAs connecting direct to our DBs)
I suppose you might set Column-level permissions on CreateDate.
A half-way-house might be to have an INSERT TRIGGER create a row in a 1:1 table, so that column was outside the main table. The second table could have SELECT permissions, where the main table has UPDATE permissions, and thus not need an UPDATE trigger to prevent changes to CreateDate - which would remove some "weight" when updating rows normally.
I suppose you coul have an UPDATE/DELETE trigger on the second table to prevent change (which would never be executed in normal circumstances, so "lightweight")
Bit of a pain to have the extra table though ... could have one table for all CreateDates - TableName, PK, CreateDate. Most database architects will hate that though ...
Certainly is.
Here is an example in action for you.
Create table #TableName
(
ID INT IDENTITY(1,1) PRIMARY KEY,
CreatedDate DATETIME NOT NULL DEFAULT GETDATE(),
SomeDate VARCHAR(100)
)
INSERT INTO #TableName (SomeDate)
SELECT 'Some data one' UNION ALL SELECT 'some data two'
SELECT * FROM #TableName
DROP TABLE #TableName
Setting the default value isn't enough, you should add a trigger to prevent updating:
CREATE TRIGGER UpdateRecord ON my_table
AFTER UPDATE AS UPDATE my_table
SET [CreatedDate] = ((SELECT TOP 1 [CreatedDate] FROM Deleted d where d.[id]=[id]))