can't make a trigger update only the relevant rows - sql

I'm working on a school project and my trigger gives me a hard time.
Its' purpose is to update the Rating field of an updated Product, but it updates all rows in Products instead.
CREATE TRIGGER Update_Rating
ON dbo.Reviews
FOR Insert
as
Update dbo.Products
set Rating=(Select [avarage_rating]=avg(r.Rating)
From dbo.Reviews as r join inserted on r.ItemNumber = inserted.ItemNumber
where r.ItemNumber = Inserted.ItemNumber)
Your help is much appreciated

What you are trying to do is calculate the average of a column, which is a number. There will not be a unique average per row. Hence the Rating column in your desired table gets updated with this value. Try changing your update condition and it should work.

Related

Average column values and show the output in another column

I have some products in a table tbProduct which has these columns:
Pcode
product
Avg_costprice
And I have another table which its name is tbStockIn and it has these columns:
Pcode
Product
Cost-price
How can I take the average of the column Cost-price according to Pcode, and show the output in the column Avg_costprice?
Please help me.
If you want to update Avg_costprice of tbProduct related to tbStockIn use this code:
UPDATE tbProduct SET
Avg_costprice = (SELECT AVG(Cost-price) FROM tbStockIn
WHERE tbStockIn.Pcode = tbProduct.Pcode)
I hope it helps you with your purpose.
I can see 3 options.
A. Don't have tbProduct
It seems like you may not need tbProduct and you could just ask for Avg_costprice when you need it:
SELECT
Pcode,
AVG(Cost-price) AS Avg_costprice
FROM tbStockIn;
B. Update tbProduct every time tbStockIn changes
If you have to have tbProduct then every time you interact with tbStockIn you'll need to update the average price. Upon a change to tbStockIn you can:
update the avg. prices only for the products you modify,
recalculate the avg-prices for the whole product table.
C. Computed column
Please see formula for computed column based on different table's column.

How to create a sql trigger to set a table column equal to a value after insert?

new to oracle and sql but trying to learn triggers. I think I'm having some syntax errors here, but let me explain what I am trying to do.
I have two tables: 1. group_membership with the columns
user_internal_id | group_internal_id (FK) | joined_time
and 2. group_details with the columns
group_internal_id (PK) | group_name | group_owner | created_time | movie_cnt | member_cnt|
(PK and FK stand for Primary Key and Foreign Key that relates to that Primary Key respectively.)
What I want to do:
After a new row is inserted into the group_membership table, I want to
update the value of member_cnt in the group_details table with the amount of times a particular group_internal_id appears in the group_membership table.
--
Now, my DBA for the app we are working on has created a trigger that simply updates the member_cnt of a particular group by reading the group_internal_id of the row inserted to group_membership, then adding 1 to the member_cnt. Which works better probably, but I want to figure out how come my trigger is having errors. Here is the code below
CREATE OR REPLACE TRIGGER set_group_size
AFTER INSERT ON group_membership
FOR EACH ROW
DECLARE g_count NUMBER;
BEGIN
SELECT COUNT(group_internal_id)
INTO g_count
FROM group_membership
GROUP BY group_internal_id;
UPDATE group_details
SET member_cnt = g_count
WHERE group_details.group_internal_id = group_membership.group_internal_id;
END;
The errors I'm receiving are:
Error(7,5): PL/SQL: SQL Statement ignored
Error(9,45): PL/SQL: ORA-00904: "GROUP_MEMBERSHIP"."GROUP_INTERNAL_ID": invalid identifier
I came here because my efforts have bene futile in troubleshooting. Hope to hear some feedback. Thanks!
The immeidate issue with your code is the update query of your trigger:
UPDATE group_details
SET member_cnt = g_count
WHERE group_details.group_internal_id = group_membership.group_internal_id;
group_membership is not defined in that scope. To refer to the value on the rows that is being inserted, use pseudo-table :new instead.
WHERE group_details.group_internal_id = :new.group_internal_id;
Another problem is the select query, that might return multiple rows. It would need a where clause that filters on the newly inserted group_internal_id:
SELECT COUNT(*)
INTO g_count
FROM group_membership
WHERE group_internal_id = :new.group_internal_id;
But these obvious fixes are not sufficient. Oracle won't let you select from the table that the trigger fired upon. On execution, you would meet error:
ORA-04091: table GROUP_MEMBERSHIP is mutating, trigger/function may not see it
There is no easy way around this. Let me suggest that this whole design is broken; the count of members per group is derived information, that can easily be computed on the fly whenever needed. Instead of trying to store it, you could, for example, use a view:
create view view_group_details as
select group_internal_id, group_name,
(
select count(*)
from group_membership gm
where gm.group_internal_id = gd.group_internal_id
) member_cnt
from group_details gd
Agree with #GMB that your design is fundamentally flawed, but if you insist on keeping a running count there is a easy solution to mutating they point out. The entire process is predicated on maintaining the count in group_details.member_count column. Therefore since that column has the previous count you do not need to count them - so eliminate the select. Your trigger becomes:
create or replace trigger set_group_size
after insert on group_membership
for each row
begin
update group_details
set member_cnt = member_cnt + 1
where group_details.group_internal_id = :new.group_internal_id;
end;
Of course then you need to handle group_membership Deletes and Updates of group_internal_id. Also, what happens when 2 users process the same group_membership simultaneously? Maintaining a running total for a derivable column is just not worth the effort. Best option just create the view as GMB suggested.

SQL Server Update Column based on value from another table

Please assist if I should use a trigger or procedure. I am trying to update the ScaleRating in table GSelfAssessment from GRatingScale if the Score in GSelfAssessment falls between the minimum and maximum score in GRatingScale.
GSelfAssessment table
GRatingScale Table
Preferably this should be achieved for each row on either update or insert. I believe SQL trigger is the most appropriate one. I understand the inserted/deleted concept inside a trigger after my research. E.g.
CREATE TRIGGER [dbo].[TR_GSelfAssessment_update] ON [dbo].[GSelfAssessment]
FOR UPDATE
AS
BEGIN
UPDATE GSelfAssessment
SET GSelfAssessment.ScaleRating= (Select )---this is where i have a problem-----
END
I believe there is Guru out here who can give me solution to this. I will learn a lot.
SQL Server supports computed columns. If you want ScaleRating to always be aligned with the rest of the data, then that is the best approach:
alter table GSelfAssessment
add ScaleRating as ( . . . );
This adds a new "column" that gets calculated when the value is used in a query. If the computation is expensive or you want to build an index, then use persisted so the value is actually stored with the rest of the data -- and recalculated when needed.
You can add the computed column in the create table statement as well. If you have already created the table, you can drop the column and re-add it or modify it.
You should not have that column. Join to the rating table when you need to. You can create a view if it makes it easier.
select …
from GSelfAssessment a
inner join
GRatingScale r
on (a.Score>r.MinScore and a.Score<=r.MaxScore)
Adjust/create view as required

Why won't my master table column increment in my trigger?

I need to create a trigger that will keep track of the number of times a movie is rented from a business like Blockbuster. I need a separate trigger for an insert, a delete and an update.
The column that tracks this number of times rented is called num_rentals and has a datatype of int. This column is part of the Movies table which has *movie_id (pk*), movie_title, release_year, movie_description, and num_rentals. The customer_rentals table has item_rental_id(pk), *movie_id(fk*), customer_id.
I searched and found a similar thread here for this and tried using the supplied answer, but to no avail. I executed the following string without any errors but I saw no change in the num_rentals column when I inserted data into either the Movie or the Customer_rentals table. What am I doing wrong?
CREATE TRIGGER dbo.tr_num_rented_insert ON dbo.customer_rentals
FOR INSERT
AS
BEGIN
UPDATE m
SET num_rentals = num_rentals + 1
FROM dbo.movies AS m
INNER JOIN inserted AS i ON m.movie_id = i.movie_id ;
END
I added the num_rentals field to the table later and I also need to know how to initialize the field value to zero for all records currently in the Movies table.
I want to understand this as much as I want the answer so assistance is greatly appreciated. I read that there may be more efficient ways to manage this type of data, but this is the way my instructor wants it. Thank you in advance!
I suspect your problem is null num_rental column. So you're adding null to 1, resulting in null.
Personally, I'd set the num_rentals column to non-nullable with a default of zero, but assuming you can't do that, use isnull().
Like so
alter TRIGGER dbo.tr_num_rented_insert ON dbo.customer_rentals
FOR INSERT
AS
BEGIN
UPDATE m
SET num_rentals = isnull(num_rentals,0) + 1
FROM dbo.movies AS m
INNER JOIN inserted AS i ON m.movie_id = i.movie_id ;
END
However, even though this works, there is a problem with it; if you add multiple rows to customer_rentals, it's only going to increment by one. Personally, I'd change the trigger to account for that.
Like this
alter TRIGGER dbo.tr_num_rented_insert ON dbo.customer_rentals
FOR INSERT
AS
BEGIN
UPDATE m
SET num_rentals = isnull(num_rentals,0) + (select COUNT(*) from inserted where movie_id = m.movie_id)
FROM dbo.movies AS m
where movie_id in (select movie_id from inserted)
END
As far as the duplicate information goes, Aaron is right that this is needlessly redundant and in my experience, this type of thing often gets out of sync. With such a simple database, the num_rentals column is overkill (to be generous), but your movie database is a contrived example to teach you a concept. Basically, sometimes you will want calculated values to be easily accessible or filtered on. Take SO rep for example, I assume they don't recalc that every time they display it.
Aaron's frequently upvoted comment suggests that you are doing something unwise. However, since you are doing what your instructor is specifying, I suggest that you look at a couple of things.
First, will your update query work for the initial rental? If the initial value is 0 it will. If the initial value is null it won't.
Second, you might be referencing the wrong table. The trigger is on customer_rentals but your trigger refers to inserted. That strikes me as odd.
Just so you know, I was one of those who upvoted Aaron's comment.

SQL trigger updating table using another table

Hi everyone I am still sort of new to SQL, I have a slight problem and maybe someone can help.
I have researched all about triggers and what I read make sense, but I can't get the answer I need from it for some reason so I will explain what I need to do
I have 2 tables Products, LabelPrint
In products there are 5 columns upc, itemcode, description, price, labelprint
In LabelPrint there are the same columns
What I need is a trigger for when a new item is created or a old item is edited and the column LabelPrint is modified to =1 for yes
I need the item in question to be copied over to labelprint table
The label print table is automatically cleaned of this items after a certain period of time, I just hope someone can give me some help in understanding how I can make this trigger work the way I hope it will
thanks brandon
I would try something like this:
CREATE TRIGGER InsertProducts
ON dbo.Products
FOR INSERT
AS
INSERT INTO dbo.LabelPrint(upc, itemcode, description, price, labelprint)
SELECT
upc, itemcode, description, price, labelprint
FROM Inserted
WHERE labelprint = 1
This first trigger will fire whenever you insert data into the table dbo.Products, and if the labelprint column is set to 1, then those rows are inserted into dbo.LabelPrint as well.
The UPDATE is a bit trickier:
CREATE TRIGGER UpdateProducts
ON dbo.Products
FOR UPDATE
AS
INSERT INTO dbo.LabelPrint(upc, itemcode, description, price, labelprint)
SELECT
i.upc, i.itemcode, i.description, i.price, i.labelprint
FROM Inserted i
INNER JOIN Deleted d ON i.itemcode = d.itemcode
WHERE i.labelprint = 1 AND d.labelprint <> 1
Here, I check to see that the row has been updated (not 100% sure if itemcode is the best column to use to link the old and new values - adapt as needed if it's not), and I make sure the old value (from the Deleted pseudo-table) is not 1 while the new value (from Inserted) is 1 - in that case, the column LabelPrint has been updated to 1 and that row will be copied into the LabelPrint table.
One word of caution: you need to be aware that SQL Server does not fire the trigger for each row that is being inserted and/or updated. The trigger will fire once for each statement - and that one statement could very well update 50 rows - in that case, the trigger fires once, and the pseudo-tables inside the trigger (Inserted, Deleted) will contain 50 rows each. Just don't ever assume that Inserted only ever contains a single row - that assumption would be totally false.