I have a stored procedure where I will calculate factors by merging some tables and updates in table 'tablea'. I have created a trigger on tablea so when there is a update those records will entered into new table 'tablea_new'. My problem with trigger is I will have 6 months factors updated at a time but they some months factors may not updated but they should entered in to new table.So trigger troubled me a lot.
So I went with an If statement to insert update records
My code for If statement is
#Action A
SET A.Factor=B.Net/B.Cost,A.Net=B.Net,A.LastModified=sysDatetime(),A.LastModifiedBy=suser_name(),A.Path=B.Path
FROM Tablea A
INNER JOIN ##TEMP3 B ON
A.Year=B.Year AND
A.Month=B.Month AND
A.Media=B.Media
IF #Action='UPDATE'
BEGIN
INSERT INTO Tablea_New (ID,Media,Year,Month,Factor,Net,UpdatedDate,UpdatedBy,FilePath)
SELECT ID,Media,Year,Month,Factor,Net,LastModified,LastModifiedBy,FilePath FROM Tablea
WHERE Media='CNN'AND YEAR=#YEAR AND Net >1
END
Can you guys give me some suggestions is there can I make some modifications or I need to use triggers :(
You can make use of the Change Data Capture (CDC) feature in SQL Server 2008 R2. This will help you to automatically store all changed rows, irrespective of the type of change (i.e. whether it is insert, update or delete).
To enable CDC for your database, use the below script
USE your_database_name
GO
EXEC sys.sp_cdc_enable_db
To enable CDC for your table, use the below script
EXEC SYS.sp_cdc_enable_table
#source_schema = N'your_schema_name',
#source_name = N'your_table_name',
#role_name = NULL
Once CDC is enabled, a new table is created under schema 'cdc' to store the changed rows, along with metadata about the change. You may directly access this table to get the required data.
Related
I need to update table1.field1 From table2.field2 everytime there is a new entry on table2. I created a trigger that does exactly that :
CREATE [dbo].[insert_trg]
on [dbo].[TheCat99]
AFTER INSERT AS
BEGIN
Update therefore.dbo.thecat99
SET factura = (SELECT dbo.pedido.num_factura
From dbo.Pedido
Where dbo.thecat99.pedido=dbo.pedido.num_pedido)
WHERE dbo.thecat99.pedido = ( SELECT dbo.pedido.num_pedido
FROM dbo.pedido
Where dbo.thecat99.pedido = dbo.pedido.Num_Pedido)
AND dbo.thecat99.factura is NULL
END
The trigger Works but it's not in production yet.I've been reading about how triggers are bad and evil, but i cant see how can i do this using stored procedures.
Is the trigger a good idea? If not, how can i do the same with a stored procedure?
Triggers are not bad nor are they evil. They are a great tool (and for some tasks, the only tool) but they do require knowledge and careful use, since they are not as simple to write as they seem at first glance.
As a general rule, Triggers should be as light-weight and effective as possible.
This is because SQL Server will not return control to whoever executed the statement that fired the trigger until the trigger is also completed.
Having said that, your update code could be written like this (that will be more efficient and more readable):
UPDATE t
SET factura = p.num_factura
FROM therefore.dbo.thecat99 t
INNER JOIN dbo.Pedido p ON t.pedido= p.num_pedido
WHERE t.factura IS NULL
However Since you are not using the inserted table, It means that every time any row gets inserted into TheCat99 all the rows where the pedido value matches the num_pedido in Pedido will be used for the update. There is still room for improvement - using the inserted table instead of thecat99 means you will only be working with the records that was just inserted to the thecat99 table, so your code will be far more efficient:
UPDATE t
SET factura = p.num_factura
FROM inserted t
INNER JOIN dbo.Pedido p ON t.pedido= p.num_pedido
WHERE t.factura IS NULL
I want to track the update changes in a table via a trigger:
CREATE TABLE dbo.TrackTable(...columns same as target table)
GO
CREATE TRIGGER dboTrackTable
ON dbo.TargetTable
AFTER UPDATE
AS
INSERT INTO dbo.TrackTable (...columns)
SELECT (...columns)
FROM Inserted
However in real production some of the update queries select rows with vague conditions and update them all regardless of whether they are actually changed, like
UPDATE Targettable
SET customer_type = 'VIP'
WHERE 1 = 1
--or is_obsolete = 0 or register_date < '20160101' something
But due to table size and to analyze, I only want to choose those actually modified data for tracking. How to achieve this goal?
My track table has many columns (so I do not prefer checking inserted and deleted column one by one) but it seldom changes structure.
I guess the following code will be useful.
CREATE TABLE dbo.TrackTable(...columns same as target table)
GO
CREATE TRIGGER dboTrackTable
ON dbo.TargetTable
AFTER UPDATE
AS
INSERT INTO dbo.TrackTable (...columns)
SELECT *
FROM Inserted
EXCEPT
SELECT *
FROM Deleted
I realize this post is a couple months old now, but for anyone looking for a well-rounded answer:
To exit the trigger if no rows were affected on SQL Server 2016 and up, Microsoft recommends using the built-in ROWCOUNT_BIG() function in the Optimizing DML Triggers section of the Create Trigger documentation.
Usage:
IF ROWCOUNT_BIG() = 0
RETURN;
To ensure you are excluding rows that were not changed, you'll need to do a compare of the inserted and deleted tables inside the trigger. Taking your example code:
INSERT INTO dbo.TrackTable (...columns)
SELECT (...columns)
FROM Inserted i
INNER JOIN deleted d
ON d.[SomePrimaryKeyCol]=i.[SomePrimaryKeyCol] AND
i.customer_type<>d.customer_type
Microsoft documentation and w3schools are great resources for learning how to leverage various types of queries and trigger best practices.
Prevent trigger from doing anything if no rows changed.
Writing-triggers-the-right-way
CREATE TRIGGER the_trigger on dbo.Data
after update
as
begin
if ##ROWCOUNT = 0
return
set nocount on
/* Some Code Here */
end
Get a list of rows that changed:
CREATE TRIGGER the_trigger on dbo.data
AFTER UPDATE
AS
SELECT * from inserted
Previous stack overflow on triggers
#anna - as per #Oded's answer, when an update is performed, the rows are in the deleted table with the old information, and the inserted table with the new information –
We have a large number of databases with the same schema, which each have a table with triggers to sync records with another table in a central database. When the table is updated, inserted into, or deleted from, the table in the central database also has a record updated, inserted, or deleted.
We've been having records mysteriously disappear from the table in the central database. When researching the problem I found that when the insert/delete trigger fires there are records in the deleted table that are not from the current delete statement. They aren't even records in the same database. They look like the old values record for update statements on the same table in another database.
All the information I could find says records in the deleted table should be from the statement that caused the trigger to fire.
Can anyone explain why I'm seeing this behavior instead?
EDIT: This is what the insert/delete trigger looks like:
DECLARE #TenantID INT
SELECT #TenantID = ID FROM [CentralDB]..Tenants WHERE db = DB_Name()
INSERT INTO [CentralDB].[dbo].[TenantUsers]
(..snipped list of columns...)
SELECT
...snipped list of columns...
FROM inserted
WHERE UserNameID NOT IN (0,6)
DELETE FROM [CentralDB]..TenantUsers WHERE UserNameID in
(SELECT UserNameID FROM DELETED WHERE UserNameID NOT IN (0,1,6))
And the update trigger:
DECLARE #TenantID INT
SELECT #TenantID = ID FROM [CentralDB]..Tenants WHERE db = DB_Name()
UPDATE [CentralDB].[dbo].[TenantUsers]
SET ...snipped list of columns...
FROM INSERTED i
WHERE i.UserNameID = TenantUsers.UserNameID
AND i.UserNameID NOT IN (0,6)
You've probably done this but if records are being deleted which ought not to be then i'd go round the db's (or write a script too) and check the triggers which contain the delete statements only fire for inserts and deletes.. Maybe there is a rouge trigger which fires on update and executes the delete command?
Its a long shot..
Other than this i would check there are no other triggers in the chain which can delete from the central db table.
there appear to be no obvious issues with the trigger design
I'm using insert-/update triggers to update a second table's column Price.
The insert trigger seems to work perfectly, but when I try to change a single record in SSMS, I get an error:
The row value(s) updated or deleted either do not make the row
unique or they alter multiple rows(2 rows).
This is my update-trigger:
CREATE TRIGGER [dbo].[trgUpdateMasterData] ON [dbo].[tabSparePartMasterData_Temp]
AFTER UPDATE
AS
UPDATE tabSparePart
SET Price = MD.Price
FROM tabSparePart INNER JOIN
(
SELECT inserted.[Material Number (SAP)] AS MaterialNumber, inserted.Price
FROM inserted
GROUP BY [Material Number (SAP)], inserted.Price
) MD
ON tabSparePart.SparePartName = MD.MaterialNumber
I need to group by Material-Number because there are redundant rows inserted into table tabSparePartMasterData_Temp which i'm only using to update the Sparepart-Price in tabSparePart. But i assumed that the group by would sort out the duplicates(Price is same for any duplicate).
It's possible that the inserted/updated records' MaterialNumber is not available in tabSparepart. In this case this record should be "skipped". Does the INNER JOIN takes that into account?
Try adding SET NOCOUNT ON to the trigger
This error doesn't look like a SQL error and I'm guessing the client code is getting confused by the 2nd update in the trigger.
Edit: this error can be caused by the data grid view in SSMS being silly.
This isn't a SQL message as I thought: it is an SSMS being stupid message
See these which all says "learn to write SQL"
http://blogs.lessthandot.com/index.php/DataMgmt/DataDesign/dealing-with-the-row-value-s-updated-or (Less than dot blog)
Trigger that modifies multiple rows on diffrent table then it was invoked on in SQL Server 2005
SSMS permits duplicate records in a table, but not subsequent updates
Saying that, there is a KB article about a bug in SQL Server 2005...
I have a table MyTable with a trigger defined like this:
ALTER TRIGGER [MyTableInsertDeleteUpdate]
ON [dbo].[MyTable]
AFTER INSERT,DELETE,UPDATE
AS
DECLARE #id int;
BEGIN
SELECT #id = ins.id FROM inserted ins;
IF (#id IS NOT NULL)
BEGIN
-- insert a new record to audit table
PRINT 'inserted/updated id: ' + CAST(#id AS VARCHAR);
END
END
I realize that if more than one rows are updated like this,
UPDATE MyTable SET name = 'test rows' WHERE id in (1, 2, 3);
the tigger is called only once and only the firstone in [inserted] is updated. Actually, [inserted] may have more than one rows (3 in this case if id 1, 2,3 exist). In order words, the trigger is not fired on each row. Is that right?
I am using Microsoft SQL Server 2005.
Yeah the trigger is fired once per statement (not once per row) that makes the changes you are subscripting to. It will even fire if no rows where affected.
http://msdn.microsoft.com/en-us/library/ms189799(SQL.90).aspx
As Hojou said, your trigger will fire once per statement rather than once per affected row. This is different to databases like Interbase and Firebird, and threw me when I first started using SQL Server.
The whole point of the inserted and deleted 'virtual' tables is because the events are record-SET based, not row-based.
There are any number of tutorials out there that cover writing sql to process the inserted/deleted tables, but watch out for the shovelware ones. I've seen more than a couple of so-called tutorials that have just been copy/pasted from another database platform and won't actually work in SQL Server as they claim to (one of the top hits for 'SQL Server trigger example' in Google gets it completely wrong for UPDATE statements).
This is a reasonable introduction to Triggers and the concepts required to make sense of the inserted and deleted tables, with an explanation of why you will be missing events in your own example. The Microsoft docs themselves are reasonably useful once you get past their dull, lifeless structure and writing-style.
To insert records to an audit table from an insert you would do something like this in the trigger:
insert auditable (field1, field2, insert_date, insertedBy)
select field1, field2, getdate(), user_Name() from inserted
No fooling around with setting variables just a a plain insert based on a select statment.
Personally I would have a separate trigger for inserts, updates and deletes as you want differnt code for each.