Update trigger with GROUP BY - sql-server-2005

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...

Related

TSql Trigger To Delete Rows from Table1 Upon Insert to Table2

I've read and tried other posts/answers but am having a hard time getting this to work. I feel like it should be pretty simple for an experienced SQL programmer, but for me I'm ready to ask for help.
When I insert a record into the HistoricalWorkorders table, I simply want to delete any records with the same workorder # in the ActiveWorkorders table.
I'm trying to accomplish this using a trigger on insert.
I started simple by using the following code to insert into ActiveWorkorders every time a record is inserted into HistoricalWorkorders. That code works.
How do I modify this query such that upon insert to the HistoricalWorkorders table, any records with the same workorder # within the ActiveWorkorders table get deleted?
On a seperate note, everyone says you can reference the inserted record simply by referencing 'inserted', but it doesn't work for me? For example, shouldn't inserted.workorder return the workorder value being inserted to the table?
ALTER trigger [dbo].[WOArchive] on [dbo].[HistoricalWorkOrders]
after update,insert
as
begin
insert into ActiveWorkorders
(Workorder,Grindcount,Status)
select i.workorder, i.grindcount, i.status
from HistoricalWorkorders t
inner join inserted i on t.workorder=i.workorder
end

SQL Server : make update trigger don't activate with no changing value

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 –

AFTER UPDATE trigger using an aggregate from the updated table

I'm having trouble with an update trigger. I want the trigger to set Quarterbacks.Yards equal to the sum of Wide receiving yards if they're on the same team.
create trigger T_Receiving_Passing
on NFL.Widereceivers
after update
as
begin
declare
#receivingyards int,
select #receivingyards = sum (Widereceivers.Yards) from NFL.Widereceivers
update NFL.Quarterbacks
set Quarterbacks.Yards = #receivingyards
where Quarterbacks.Team = Widereceivers.Team
end
At the end of the statement, Widereceivers.Team is underlined in red, and it is causing errors. I get this same error whenever I try to reference a column in another table without naming the table in a from clause. How can I fix this problem?
Ok, you should be able to do this without the SELECT statement or its variable and instead use a more complex UPDATE statement joining on the special inserted table which holds the new values from the update.
CREATE TRIGGER T_Receiving_Passing
ON NFL.Widereceivers
AFTER UPDATE
AS
UPDATE NFL.Quarterbacks
-- Get the SUM() in a subselect
SET Quarterbacks.Yards = (SELECT SUM(Yards) FROM Widereceivers WHERE Team = inserted.Team)
FROM
NFL.Quarterbacks
-- Join against the special inserted table
JOIN inserted ON Quarterbacks.Team = inserted.Team
GO
Here is a proof of concept
In your original attempt, you hoped to use a SELECT query first to populate a scalar variable. In an UPDATE statement however, you can use a subselect that returns exactly one column of exactly one row inside the SET clause to retrieve a new value.
Since your requirement was to use an aggregate SUM() it isn't as straightforward as assigning a value directly from the inserted like SET Yards = inserted.Yards. Instead, the subselect produces the aggregate sum limited to just the Team used in the inserted row.
As far as the inserted/deleted tables go, review the official documentation. I have not worked with SQL Server regularly for a few years but if I recall correctly, the inserted table must occur in the FROM clause which implies it will usually need to be JOINed in. In your UPDATE statement, inserted is needed in both the subselect and the outer query, so it was joined in the outer one.

If statement on update statement Instead of trigger?

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.

Need some help with Sql Server and a simple Trigger

I wish to make a trigger but i'm not sure how to grab the data for whatever caused the trigger.
I have a simlpe table.
FooId INT PK NOT NULL IDENTITY
Name VARCHAR(100) NOT NULL
I wish to have a trigger so that when an UPDATE, INSERT or DELETE occurs, i then do the following.
Pseduocode
IF INSERT
Print 'Insert' & Name
ELSE IF UPDATE
Print 'Update' & FooId & Name
ELSE IF DELETE
Print 'Delete' & FooId & Name
Now, I know how to make a trigger for a table.
What i don't know how to do is figure out the values based on what the trigger type is.
Can anyone help?
Edit: Not sure if it helps, but db is Sql Server 2008
the pseudo table "inserted" contains the new data, and "deleted" table contains the old data.
You can do something like
create trigger mytrigger on mytable for insert, update, delete
as
if ( select count(*) from inserted ) > 0
-- insert or update
select FooId, Name from inserted
else
-- delete
select FooId, Name from deleted
To clarify all the comments made by others, on an insert, the inserted table contains data and deleted is empty. On a delete, the situation is reversed. On an update, deleted and inserted contain the "before" and "after" copy of any updated rows.
When you are writing a trigger, you have to account for the fact that your trigger may be called by a statement that effects more than one row at a time.
As others have pointed out, you reference the inserted table to get the values of new values of updated or inserted rows, and you reference the deleted table to get the value of deleted rows.
SQL triggers provide an implicitly-defined table called "inserted" which returns the affected rows, allowing you to do things like
UPDATE mytable SET mytimestamp = GETDATE() WHERE id IN (SELECT id FROM inserted)
Regarding your code sample, you'll want to create separate INSERT, UPDATE and DELETE triggers if you are performing separate actions for each.
(At least, this is the case in SQL Server... you didn't specify a platform.)
On 2008, there is also MERGE command. How do you want to handle it?
Starting from 2008, there are four commands you can modify a table with:
INSERT, UPDATE, DELETE, and MERGE:
http://blogs.conchango.com/davidportas/archive/2007/11/14/SQL-Server-2008-MERGE.aspx
http://sqlblogcasts.com/blogs/grumpyolddba/archive/2009/03/11/reasons-to-move-to-sql-2008-merge.aspx
What do you want your trigger to do when someone issues a MERGE command against your table?