Database trigger to update a row on parent table after child table has been updated - sql

First time creating a Database trigger. I have a child table that when its cost column is updated, I need its parent table to also update its cost column to reflect the change.
Here is my sorry attempt so far. That's obviously not working. I am having a problem figuring out how to extract the total cost as a variable and store it in the parent table.
My current approach assumes a static id vaule at the moment. I am not entirely sure how to dynamically determine the id value of the row that was updated.
CREATE TRIGGER ParentCost_Update
ON ChildTable
AFTER INSERT, UPDATE
AS
SELECT SUM(Cost) AS TotalCost FROM ChildTable where parent_id=2080
UPDATE ParentTable
SET Cost=TotalCost
where id=parent_id;
GO
This current script is returning an error
Msg 207, Level 16, State 1, Procedure ParentCost_Update, Line 9
Invalid column name 'TotalCost'.

You need to be careful in triggers, in that there could be more than one row updated. Therefore, you need to do row-based handling.
To obtain the newly inserted / updated row, use the inserted and deleted pseudo rows.
You will almost certainly also going to need to implement a deleted trigger as well, viz, if a row is removed from a child table that the parent will need to be recalculated.
Here's a row based take, using a CTE to map your two-step process above , as follows:
CREATE TRIGGER ParentCost_Update
ON ChildTable
AFTER INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON;
WITH cteParentsAffected AS
(
SELECT ins.parent_id
FROM inserted ins
UNION
SELECT del.parent_id
FROM deleted del
)
, cteTotal AS
(
SELECT ct.parent_id, SUM(ct.Cost) AS TotalCost
FROM ChildTable ct
INNER JOIN cteParentsAffected par
ON ct.parent_id = par.parent_id
GROUP BY ct.parent_id
)
UPDATE pt
SET Cost=cte.TotalCost
FROM ParentTable pt
INNER JOIN cteTotal cte
ON id=cte.parent_id;
GO
With a SqlFiddle here

Try this
Update parenttable set total= (select sum(total) from childtable c where c.id= parent table.id)
Where id in (select id from inserted)
Change the table and column names.

Related

SQL insert trigger for insert with update

I have two tables:
[1] Donations - with amount and pet_id field
[2] Pets - with id and total donations field
I'm trying to create a trigger which will update total donations field whenever
a new row is being inserted to Donations table. I tried this one:
create trigger update_donations
on sponserships
for insert
as
update dbo.Pets
set tot_donations = (
select new_val = inserted.amount + pets.tot_donations
from inserted
where inserted.[Pet-ID] = pets.[Animal-ID]
)
But of course it changes all records, whereas i want to change only records that are changed in the donations table.
It is usually not a good practice to store this type of derived information - you could have a view that computes it on the fly rather than a trigger that keeps it up to date. Also please note that if you go that way, you also need a delete and an update trigger...
That said, you can use the following query in your insert trigger to update the relevant record in the pets table:
update p
set p.total_donations = p.total_donations + i.amount
from dbo.Pets p
inner join inserted i on i.[Pet-ID] = p.[Animal-ID]

Add data to trigger from the last insert

I am faced with the following situation:
I created a trigger which reacts on insert to the third table. When I insert any data (for example 1 1 2), last number should be subtracted from the Amount of stock column from cell, which has necessary ID Product (as it's shown on picture). But how can I understand which row was the last added? I thought firstly to do it by select, but it seems impossible. And now I think that it's possible to do it with the help of cursor, but it doesn't seem as the best variant. Is there a better variant how can I do it?
Here's my code of trigger, but it only subtracts 1 from the 1st product each time, unfortunately:
CREATE TRIGGER AmountInsert ON Amount
AFTER INSERT
AS
BEGIN
UPDATE Product
SET Amount_On_Stock =
(SELECT Amount_On_Stock FROM Product
WHERE ID_Product = 1) - 1
WHERE ID_Product = 1
END
The first thing you need to understand is that in a trigger in SQL Server you are provided with an inserted pseudo-table and a deleted pseudo-table. You use these tables to determine what changes have occurred.
I think the following trigger accomplishes what you are looking for - the comments explain the logic.
CREATE TRIGGER dbo.AmountInsert ON dbo.Amount
AFTER INSERT
AS
BEGIN
set nocount on;
update P set
-- Adjust the stock level by the amount of the latest insert
Amount_On_Stock = coalesce(Amount_On_Stock) - I.Amount
from dbo.Product P
inner join (
-- We need to group by ID_Product in case the same product appears in the insert multiple times
select ID_Product, sum(Amount) Amount
from Inserted
group by ID_Product
-- No need to update is net change is zero
having sum(Amount) <> 0
) I
on I.ID_Product = P.ID_Product;
END

Sql Server - Update multiple rows based on the primary key (Large Amount of data)

struggling with this one, quite a lengthy description so ill explain best I can:
I have a table with 12 columns in, 1 being a primary key with identity_insert, 1 a foreign key , the other 10 being cost values, ive created a statement to group them into 5 categories, shown below:
select
(ProductID)ProjectID,
sum(Cost1)Catagory1,
sum(Cost2)Catagory2,
sum(Cost3 + Cost4 + Cost5 + Cost6 + Cost7) Catagory3,
sum(Cost 8 + Cost 9)Catagory4,
sum(Cost10)Catagory5
from ProductTable group by ProductID
ive changed the names of the data to make it more generic, they aren't actually called Cost1 etc by the way ;)
the foreign key can appear multiple times (ProductID) so in the above query the related fields are calculated together based upon this... Now what ive been trying to do is put this query into a table, which i have done successfully, and then update the data via a procedure. the problem im having is that all the data in the table is overwritten by row 1 and when theres is thousands of rows this is a problem.
I have also tried putting the above query into a view and the same result... any suggestions would be great :)
update NewTable set
ProductID = (ProductView.ProductID ),
Catagory1 = (ProductView.Catagory1 ),
Catagory2 = (ProductView.Catagory2 ),
Catagory3 = (ProductView.Catagory3 ),
Catagory4 = (ProductView.Catagory4 ),
Catagory5 = (ProductView.Catagory5 )
from ProductView
I need something along the lines like above.... but one that doesn't overwrite everything with row 1 haha ;)
ANSWERED BY: Noman_1
create procedure NewProducts
insert into NewTable
select ProductID.ProductTable,
Catagory1.ProductView,
Catagory2.ProductView,
Catagory3.ProductView,
Catagory4.ProductView,
Catagory5.ProductView
from ProductView
inner join ProductTable on ProductView.ProductID = ProductTable.ProductID
where not exists(select 1 from NewTable where ProductView.ProductID = NewTable.ProductID)
above procedure locates the new Product that has been created within a view, the procedure query detects that there is a Product that is not located in the NewTable and inserts it via the procedure
As far as i know, and since you want to update all the products in the table, and each product uses all the sums of the product itself from origin, you actually need to update each row 1 by 1, and as consecuence when you do an update like the next, its your only main way
update newtable
set category1 = (select sum(cost1) from productTable where productTable.productId = newtable.ProductID),
category2 = (select sum(cost2) from productTable where productTable.productId = newtable.ProductID),
etc..
Keep in mind that if you have new products, they wont get inserted with the update, you would need like this in order to add them:
Insert into newtable
Select VALUES from productTable a where productId not exists(select 1 from newTable b where a.ProductId = b.ProductId);
A second way, and since you want allways to update all the data, is to simply truncate and do a insert select right after.
Maybe on an Oracle, you would be albe to use a MERGE but im unaware if it would really improve anything.
I asume that simply having a view would not work due the amount of data you state you have.
EDIT, I never knew that the MERGE STATMENT is actually avaiable on SQL Server 2008 and above, with this single statment you could do an UPDATE/INSERT on all but it's efficiency is unknown to me, you may want to test it with your high amount of data:
MERGE newtable AS TARGET
USING select ProductId, sum(cost1) cat1, sum(cost2) cat2 ...
FROM productTable Group by ProductId AS SOURCE
ON TARGET.ProductId = SOURCE.ProductID
WHEN MATCHED
THEN UPDATE SET TARGET.category1 = cat1, TARGET.category2 = cat2...
WHEN NOT MATCHED
THEN INSERT (ProductId, category1, category2,...)
VALUES (SOURCe.ProductId, SOURCE.cat1, SOURCE.cat2...);
More info about merge here:
http://msdn.microsoft.com/library/bb510625.aspx
The example at the end may give you a good overview of the sintax
You haven't given any join condition. SQL Server cannot know that you meant to update rows matched by productid.
update NewTable set
ProductID = (ProductView.ProductID ),
Catagory1 = (ProductView.Catagory1 ),
Catagory2 = (ProductView.Catagory2 ),
Catagory3 = (ProductView.Catagory3 ),
Catagory4 = (ProductView.Catagory4 ),
Catagory5 = (ProductView.Catagory5 )
from NewTable
join ProductView pv on NewTable.productid = pv.productid
You don't need a view. Just past the view query to the place where I said ProductView. Of course, you can use a view.

Using trigger in one table and updating another table

CREATE TRIGGER dbo.updateTrigger
ON dbo.Education
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF NOT (UPDATE( HighestDegreeDoc) OR UPDATE (GPA) OR UPDATE (CreditHours))
RETURN
UPDATE dbo.School
set Uploaded =1
from dbo.School
JOIN inerted i ON i.Uploaded = School.Uploaded
END
GO
What is Wrong with this code. i am trying to update field in School table , field uploaded when (HighestDegreeDoc , GPA, CrediHours) update in Education Table. NOTE Education Table has more than 15 field, (Uploaded Field in School table update only when these 3 field change)
Just a guess....
JOIN inerted i ON i.Uploaded = School.Uploaded
Should be...
JOIN inserted i ON i.SchoolId = School.SchoolId
Looks like Updated is some sort of flag that you are setting. You probably want to join on an ID column instead.
It's only a typo error : it's join inserted ;)
And I don't think your join condition is good.
Can you give us the ddl ?

how to delete duplicates from a database table based on a certain field

i have a table that somehow got duplicated. i basically want to delete all records that are duplicates, which is defined by a field in my table called SourceId. There should only be one record for each source ID.
is there any SQL that i can write that will delete every duplicate so i only have one record per Sourceid ?
Assuming you have a column ID that can tie-break the duplicate sourceid's, you can use this. Using min(id) causes it to keep just the min(id) per sourceid batch.
delete from tbl
where id NOT in
(
select min(id)
from tbl
group by sourceid
)
delete from table
where pk in (
select i2.pk
from table i1
inner join table i2
on i1.SourceId = i2.SourceId
)
good practice is to start with
select * from … and only later replace to delete from …