How to Create Trigger to Keep Track of Last Changed Data - sql

CREATE TABLE Member
(
memberID - PK
memberName
dateRegistered - one time process
);
CREATE TABLE MemberLastChanged
(
memberID
memberName
dateEntered
);
If by any chance a user changes his member name, i need to keep track of the currently changed memberName in a history table.
For example, current info is:
memberID: 5534 memberName: james
User changes it to:
memberID: 5534 memberName:
mark
By now, "Member" will hold current values:
5534 and mark
AND
"MemberLastChanged" will hold:
5534 and james
How can i achieve this in t-sql using trigger?

CREATE TRIGGER TRG_Member_U ON Member FOR UPDATE
AS
SET NOCOUNT ON
INSERT MemberLastChanged (memberID, memberName)
SELECT
D.memberID, D.memberName
FROM
DELETED D JOIN INSERTED I ON D.memberID = I.memberID
WHERE
D.memberName <> I.memberName
GO
Also, add a default of GETDATE to dateRegistered so it's recorded automatically.
This also filters out dummy updates by comparing new and old values (INSERTED vs DELETED).
INSERTED and DELETED are special tables available only in trigger.

You create an UPDATE trigger - triggers have access to two logical tables that have an identical structure to the table they are defined on:
INSERTED, which is the new data to go into the table
DELETED, which is the old data the is in the table
See this MDSN article on using these logical tables.
With this data you can populate your history table.
CREATE TRIGGER trg_Member_MemberUpdate
ON dbo.Member AFTER UPDATE
AS
INSERT INTO dbo.MemberLastChanged(memberID, memberName)
SELECT d.MemberID, d.MemberName
FROM DELETED d

You want to have an AFTER UPDATE trigger on your users table - something like:
CREATE TRIGGER trg_MemberUpdated
ON dbo.Member AFTER UPDATE
AS BEGIN
IF UPDATE(memberName)
INSERT INTO
dbo.MemberLastChanged(memberID, memberName, dateEntered)
SELECT
d.MemberID, d.MemberName, GETDATE()
FROM
Deleted d
END
Basically, this trigger checks to see whether the memberName property was updated; if so, a row with the old values (which are available in the Deleted pseudo table inside the UPDATE trigger) is inserted into MemberLastChanged

Related

Using Trigger to insert record in another table, AND write back the ID into original record ..?

I have a simple trigger setup, which is used to insert records into a RentJournal table, whenever there is a record inserted in the UnitAGA table.
The RentJournal table has a primary key ID column named RentJournalID, which is auto incrementing. The UnitAGA table also has a nullable foreign key column named RentJournalID, which links each UnitAGA entry, to its corresponding entry in RentJournal table (which is inserted through the Trigger below).
Problem is that currently this Trigger is only inserting values into RentJournal table. But now I want to also fetch the ID assigned for each RentJournal entry through this Trigger, and write that into the corresponding UnitAGA record, whose insert actually triggered the Trigger in the first place. How do I do this ?
The Trigger code as of right now is this:
USE [RentDB]
GO
ALTER TRIGGER [RTS].[InsertRentJournalEntry]
ON [RTS].[UnitAGA]
AFTER INSERT
AS
BEGIN
INSERT INTO RTS.RentJournal
(UnitId, AdjustmentType, EffectiveDate, ReferenceFormNo)
SELECT
UnitId, 'AGA', EffectiveDate, ReferenceFormNo FROM inserted
END
Have a look at the INSERT logical table that is available in insert triggers:
DML triggers use the deleted and inserted logical (conceptual) tables. They are structurally similar to the table on which the trigger is defined, that is, the table on which the user action is tried. The deleted and inserted tables hold the old values or new values of the rows that may be changed by the user action. For example, to retrieve all values in the deleted table, use: SELECT * FROM deleted
http://technet.microsoft.com/en-us/library/ms189799.aspx
Then use ##IDENTITY to get the value of the identity column on your RentJournal table.
So you should be able to do something like:
update INSERTED set RentJournalID = ##IDENTITY

Firebird - using before insert trigger for a master table to sum detail records

I have two tables a master and detail. the detail table records are created automatically using a trigger after insert new master record.
But I need to create Before Insert or Update trigger (call it T1) for the master table to do some calculations based on fields from master record and sum from its detail records.
My problem to be able to do my calculations in T1 I need to insert details records first but of course the detail records has foreign key constraint to the master table ID which prevent this action so what do you think the best approach to achieve this task ?
I think the best approach is to use a stored procedure that does all the work...
something like this:
create procedure insert_record(id integer, ...);
as
begin
/* this inserts master and through triggers creates detail */
insert into master (id, ... )
values (:id, ...);
/* calculate values */
select sum(...) from detail
where id = :id
into :calculation;
/* usa calculated value to update master table */
update master
set calculated_value = :calculation
here id = :id;
end

Need some help in creating a query in SQL?

I have 6 tables:
Staff ( StaffID, Name )
Product ( ProductID, Name )
Faq ( FaqID, Question, Answer, ProductID* )
Customer (CustomerID, Name, Email)
Ticket ( TicketID, Problem, Status, Priority, LoggedTime, CustomerID* , ProductID* )
TicketUpdate ( TicketUpdateID, Message, UpdateTime, TicketID* , StaffID* )
Question to be answered:
Given a Product ID, remove the record for that Product. When a product is removed all associated FAQ can stay in the database but should have a null reference in the ProductID field. The deletion of a product should, however, also remove any associated tickets and their updates. For completeness deleted tickets and their updates should be copied to an audit table or a set of tables that maintain historical data on products, their tickets and updates. (Hint: you will need to define a additional table or set or tables to maintain this audit information and automatically copy any deleted tickets and ticket updates when a product is deleted). Your audit table/s should record the user which requested the deletion and the timestamp for the deletion operation.
I have created additional maintain_audit table:
CREATE TABLE maintain_audit(
TicketID INTEGER NOT NULL,
TicketUpdateID INTEGER NOT NULL,
Message VARCHAR(1000),
mdate TIMESTAMP NOT NULL,
muser VARCHAR(128),
PRIMARY KEY (TicketID, TicketUpdateID)
);
Addittionally I have created 1 function and trigger:
CREATE OR REPLACE FUNCTION maintain_audit()
RETURNS TRIGGER AS $BODY$
BEGIN
INSERT INTO maintain_audit (TicketID,TicketUpdateID,Message,muser,mdate)
(SELECT Ticket.ID,TicketUpdate.ID,Message,user,now() FROM Ticket, TicketUpdate WHERE Ticket.ID=TicketUpdate.TicketID AND Ticket.ProductID = OLD.ID);
RETURN OLD;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER maintain_audit
BEFORE DELETE
ON Product
FOR EACH ROW
EXECUTE PROCEDURE maintain_audit()
DELETE FROM Product WHERE Product.ID=30;
When I run this all I get this :
ERROR: null value in column "productid" violates not-null constraint
CONTEXT: SQL statement "UPDATE ONLY "public"."faq" SET "productid" = NULL WHERE $1 OPERATOR(pg_catalog.=) "productid""
GUYS,Could you help me in sorting out this problem?
What you probably want is triggers. Not sure what RDBMS you are using, but that's where you should start. I started from zero and had triggers up and running in a somewhat similar situation within an hour.
In case you don't already know, triggers do something after a specific type of query happens on a table, such as an insert, update or delete. You can do any type of query.
Another tip I would give you is not to delete anything, since that could break data integrity. You could just add an "active" boolean field, set active to false, then filter those out in most of your system's queries. Alternatively, you could just move the associated records out to a Products_archive table that has the same structure. Easy to do with:
select * into destination from source where 1=0
Still, I would do the work you need done using triggers because they're so automatic.
create a foreign key for Ticket.product_id, and TicketUpdate.Ticket_id which has ON DELETE CASCADE. This will automatically delete all tickets and ticketupdates when you delete the product.
create an audit table for Product deleters with product_id, user and timestamp. audit tables for ticket,ticketUpdate should mirror them exactly.
create a BEFORE DELETE TRIGGER for table Ticket which copies tickets to the audit table.
Do the same for TicketUpdate
Create an AFTER DETETE Trigger on Products to capture who requested a product be deleted in the product audit table.
In table FAQ create Product_id as a foreign key with ON DELETE SET NULL

Sql Server trigger insert values from new row into another table

I have a site using the asp.net membership schema. I'd like to set up a trigger on the aspnet_users table that inserted the user_id and the user_name of the new row into another table.
How do I go about getting the values from the last insert?
I can select by the last date_created but that seems smelly. Is there a better way?
try this for sql server
CREATE TRIGGER yourNewTrigger ON yourSourcetable
FOR INSERT
AS
INSERT INTO yourDestinationTable
(col1, col2 , col3, user_id, user_name)
SELECT
'a' , default , null, user_id, user_name
FROM inserted
go
You use an insert trigger - inside the trigger, inserted row items will be exposed as a logical table INSERTED, which has the same column layout as the table the trigger is defined on.
Delete triggers have access to a similar logical table called DELETED.
Update triggers have access to both an INSERTED table that contains the updated values and a DELETED table that contains the values to be updated.
You can use OLDand NEW in the trigger to access those values which had changed in that trigger. Mysql Ref
In a SQL Server trigger you have available two psdeuotables called inserted and deleted. These contain the old and new values of the record.
So within the trigger (you can look up the create trigger parts easily) you would do something like this:
Insert table2 (user_id, user_name)
select user_id, user_name from inserted i
left join table2 t on i.user_id = t.userid
where t.user_id is null
When writing triggers remember they act once on the whole batch of information, they do not process row-by-row. So account for multiple row inserts in your code.
When you are in the context of a trigger you have access to the logical table INSERTED which contains all the rows that have just been inserted to the table. You can build your insert to the other table based on a select from Inserted.
Create
trigger `[dbo].[mytrigger]` on `[dbo].[Patients]` after update , insert as
begin
--Sql logic
print 'Hello world'
end

what is the correct syntax for creating a database trigger for insert, modify and delete

i have what seems like a basic scenario for a db trigger in SQL server and i am running into an issue.
i have table Users (id, name, phone, etc) and i have tables UsersHistory (id, user_id action, fields, timestamp)
i want a database trigger where anytime inserts, updates or deletes into Users, i want a new record created in UsersHistory with the user id and the action that was done (insert new, updated fields, deleted id. Basically an audit log table.
this is how far i got, but i can't figure out how to:
Get the id on modify and deletes and also
How to get a list of fields that have changed and the action that was committed (insert, delete, update)
CREATE TRIGGER Update_Users_History
ON Users
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
-- Insert statements for trigger here
insert into UsersHistory (user_id, [action], [fields], timestamp)
select max(id) as user_id, {action ??},{fields??} getdate() from Users)
END
GO
any suggestions?
The easiest might be to just simply create three triggers - one for each operation:
CREATE TRIGGER trgUserInsert
ON dbo.User AFTER INSERT
AS BEGIN
INSERT INTO dbo.UserHistory............
END
CREATE TRIGGER trgUserDelete
ON dbo.User AFTER DELETE
AS BEGIN
INSERT INTO dbo.UserHistory............
END
CREATE TRIGGER trgUserUpdate
ON dbo.User AFTER UPDATE
AS BEGIN
INSERT INTO dbo.UserHistory............
END
That way, things are simple and you easily understand what you're doing, plus it gives you the ability to turn off a trigger for a single operation, if you e.g. need to insert or delete a huge list of items.
Inside the trigger, you have two "pseudo-tables" - Inserted (for INSERT and UPDATE) and Deleted (for UPDATE and DELETE). These pseudo tables contain the values for the newly inserted values (or the updated ones in UPDATE), or the ones that were deleted (for DELETE) or have been updated (the old values, before the update, for the UPDATE operation).
You need to be aware that a trigger will be called once even if you update a huge number of rows, e.g. Inserted and Deleted will typically contain multiple rows.
As a sample, you could write a "AFTER INSERT" trigger like this (just guessing what your table structure might be....):
CREATE TRIGGER trgUserInsert
ON dbo.User AFTER INSERT
AS BEGIN
INSERT INTO
dbo.UserHistory(UserID, Action, DateTimeStamp, AuditMessage)
SELECT
i.UserID, 'INSERT', getdate(), 'User inserted into table'
FROM
Inserted i
END
You are looking for a way to find out which "action" this trigger caused? I don't see any way to do this - another reason to keep the three trigger separate. The only way to find this out would be to count the rows in the Inserted and Updated tables:
if both counts are larger than zero, it's an UPDATE
if the Inserted table has rows, but the Deleted does not, it's an INSERT
if the Inserted table has no rows, but the Deleted does, it's a DELETE
You're also looking for a "list of fields that were updated" - again, you won't have any simple solution, really. You could either just loop through the fields in the "Users" table that are of interest, and check
IF UPDATE(fieldname) ......
but that gets a bit tedious.
Or you could use the COLUMNS_UPDATED() function - this however doesn't give you a nice list of column names, but a VARBINARY in which each column is basically one bit, and if it's turned on, that column was updated. Not very easy to use.....
If you really want to create a single, big trigger, this could serve as a basis - it detects what operation has caused the trigger to fire, and will insert entries into your User_History table:
CREATE TRIGGER trgUser_Universal
ON dbo.Users
AFTER INSERT, UPDATE, DELETE
AS BEGIN
DECLARE #InsHasRows BIT = 0
DECLARE #DelHasRows BIT = 0
IF EXISTS(SELECT TOP 1 * FROM INSERTED)
SET #InsHasRows = 1
IF EXISTS(SELECT TOP 1 * FROM DELETED)
SET #DelHasRows = 1
DECLARE #TriggerAction VARCHAR(20)
IF #InsHasRows = 1 AND #DelHasRows = 1
SET #TriggerAction = 'UPDATE'
ELSE
IF #InsHasRows = 1
SET #TriggerAction = 'INSERT'
ELSE
SET #TriggerAction = 'DELETE'
IF #InsHasRows = 1
INSERT INTO dbo.UsersHistory(user_id, [action], [fields], timestamp)
SELECT i.UserId, #TriggerAction, null, getdate()
FROM INSERTED i
ELSE
INSERT INTO dbo.UsersHistory(user_id, [action], [fields], timestamp)
SELECT d.UserId, #TriggerAction, null, getdate()
FROM DELETED d
END
I haven't included the figuring out which fields have been updated part just yet - that's left as an exercise to the reader :-)
Does that help at all?
There are two "tables" that are used in the trigger. One is DELETED and one is INSERTED. When you delete a row, that row is captured in the DELETED table. When you insert a row, that row is captured in the INSERTED table. When you update a row, the old row is in the DELETED table, and the new row is in the INSERTED table. The DELETED and INSERTED tables have the same schema as the table on which you are adding the trigger.
You might check out this solution that will create a query for you that will make all the auditing triggers you want, as well as the table in which to store the audits, excluding any selected tables. It will only do UPDATE triggers, but could easily be modified to make INSERT and DELETE triggers as well.