kondzik
kondzik

Reputation: 23

How to update parent table column value on child table insert/update/delete?

I have to related tables like in the example script below:

-- Creating table 'Product'
CREATE TABLE [dbo].[Product] (
    [Id] int IDENTITY(1,1) NOT NULL,
    [Text] nvarchar(max)  NOT NULL,
    [AvgRating] decimal(18,2)  NOT NULL
);
GO

-- Creating table 'Review'
CREATE TABLE [dbo].[Review] (
    [Id] int IDENTITY(1,1) NOT NULL,
    [Text] nvarchar(max)  NOT NULL,
    [Rating] decimal(18,2)  NOT NULL,
    [Product_Id] int  NOT NULL
);
GO

-- Creating primary key on [Id] in table 'Product'
ALTER TABLE [dbo].[Product]
ADD CONSTRAINT [PK_Product]
    PRIMARY KEY CLUSTERED ([Id] ASC);
GO

-- Creating primary key on [Id] in table 'Review'
ALTER TABLE [dbo].[Review]
ADD CONSTRAINT [PK_Review]
    PRIMARY KEY CLUSTERED ([Id] ASC);
GO

-- Creating foreign key on [Product_Id] in table 'Review'
ALTER TABLE [dbo].[Review]
ADD CONSTRAINT [FK_ProductReview]
    FOREIGN KEY ([Product_Id])
    REFERENCES [dbo].[Product]
        ([Id])
    ON DELETE NO ACTION ON UPDATE NO ACTION;

-- Creating non-clustered index for FOREIGN KEY 'FK_ProductReview'
CREATE INDEX [IX_FK_ProductReview]
ON [dbo].[Review]
    ([Product_Id]);
GO

I would like to compute AvgRating on Product row when user inserts/updates/deletes review. The most obvious and brute force approach that comes to my mind is to pull data from database and compute the average on client side, and then update the product row manually. Is it possible to do it automatically on database server without having to pull the data? If so, how? Database is hosted on MS SQL Server 2008.

Upvotes: 2

Views: 2370

Answers (1)

Code Magician
Code Magician

Reputation: 24022

You could create a trigger on the Review table. After an update on Review it could recompute the average review.

CREATE TRIGGER TRG_REVIEW 
ON Review
AFTER INSERT, UPDATE, DELETE
AS 

;WITH ratings 
AS 
(
    SELECT product_id, AVG(Rating) AS rating
    FROM Review R
    WHERE EXISTS (SELECT * FROM INSERTED WHERE product_id = R.product_id AND UPDATE(rating))
         OR EXISTS (SELECT * FROM DELETED WHERE product_id = R.product_id)
    GROUP BY product_id
)
UPDATE P set AvgRating = COALESCE(R.rating,0)
FROM Product P 
INNER JOIN ratings R 
ON P.id = R.Product_id 

Upvotes: 3

Related Questions