SQL trigger updating table using another table - sql

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.

Related

Create Sql Trigger to add new rows based on field in other table

I need to create trigger that add new row in table when the value in other table is upadated.
For example, If I change the value into 5 in table1, I need to create in other table (table2) five (5) new row with some values of table1 (for example ID or get also other value).
Example:
Table1
ID Date Flag NumberOfNewRowsInTable2
1 17/07/2017 1 0
When change into 5 the value of column "NumberOfNewRowsInTable2" I need to create
in Table2 new five rows:
Table2
ID Field1 Field2
1 X Y
1 X Y
1 X Y
1 X Y
1 X Y
Thank you all, sorry for my bad explain.I use SQL Server. Now I try to explain it better. I need to create a payment by instalments and so I want the user to choose a number of instalments and automatically I would have in the table2 (payment instalments table) the equivalent number of rows (one for each instalments) . Now, for example: There is the Table1. In the Table1 there is an ID field and I can choose the type of payments for the record. When the user choose the payment by instalments, he can choose also the number of instalments. When the user select the desired number of instalments, automatically I would have in the table2 (payment instalments table) the equivalent number of rows (one for each instalments) and the corrisponding ID (in the field ID of Table2). The other problem is this: when user remove the payment by instalments and insert other kind of payment, I need to remove it from the Table2. Moreover, if the user change the number of instalments, I need to adjust the rows with the new equivalent number of instalments. I hope to explain it better and thank you all for your answers. Andrea
It is possible to do this with a trigger?
Thank you all,
Andrea.
This certainly can be done with a trigger. You didn't specify which RDBMS you're using in the question, but my knowledge is in T-SQL, so that's what this answer will be written in.
First off, it looks like the number of rows you want to insert is dynamic based on that column value, so you'll want a Numbers table in your database if you don't have one already. That will make it considerably easier to add as many rows as you need without trying to rely on loops. You can find some methods of properly creating and populating a Numbers table here, but I just made something small for this example:
CREATE TABLE dbo.Numbers
(
ID bigint
)
INSERT INTO dbo.Numbers
VALUES (1), (2), (3), (4), (5)
Next, the trigger itself. T-SQL triggers will only be triggered by changes to the table that they are created on, so you want to create the trigger on Table1. The contents of the trigger can then insert/update/delete rows in any number of other tables. I only say this explicitly because the phrasing of your question seems to imply that you were thinking of it the other way around -- that the trigger would be on Table2 -- and I apologize if I'm reading too much into that. Anyhow, the code for the trigger:
CREATE TRIGGER dbo.tgrTable1_Update
ON dbo.Table1
AFTER UPDATE
AS
BEGIN
INSERT INTO dbo.Table2
(
ID,
Field1,
Field2
)
SELECT
inserted.ID,
'X',
'Y'
FROM inserted
JOIN deleted
ON deleted.ID = inserted.ID
JOIN dbo.Numbers n
ON n.ID <= inserted.NumberOfNewRowsInTable2
WHERE inserted.NumberOfNewRowsInTable2 != deleted.NumberOfNewRowsInTable2
END
The join condition on the Numbers table ensures that you're only adding the number of rows that you need, and nothing more. You can tweak the joins and where clause to fit the conditions that you actually need.
Edit: Thanks for adding some extra detail to your question. A trigger like this should still be able to do what you describe. You'll just need to add some logic to the contents of the trigger to handle the scenarios that you foresee. You might want to consider changing the contents of the SELECT to contain the installment calculations, or using a soft-delete column on Table2 so you're not adding and deleting rows constantly in case that matters for historical data. You can also do multiple insert/update statements within one trigger, so you should be able to fit all of your required logic into this structure.

can't make a trigger update only the relevant rows

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.

Oracle: After update Trigger

I have 3 tables:
Category(CategoryId, Name)
Product(ProductId, Name, Description, CategoryId)
OrderItem(OrderId, OrdinalNumber, ProductId, CategoryId)
I want to create an AFTER UPDATE trigger that changes CategoryId (based on new ProductId) in OrderItem after update of ProductId in OrderItem.
Can somebody help with this trigger?
Duplicating the category ID in the order line isn't something you'd usually want to do, but if you're set on that, you need a 'before' trigger, not an 'after' one - since you need to change a value in the row being updated:
create or replace trigger orderitem_cat_trig
before insert or update on orderitem
for each row
begin
select categoryid
into :new.categoryid
from product
where productid = :new.productid;
end;
/
I've made it both insert and update on the assumption you'll want to set the value for new order items too.
Unless you like database deadlocks, general performance issues, data corruption and unpredictable results, this type of updates is not advisable. If your performance is a problem, check indexes and queries. Do not replicate your columns in tables, especially not when they're part of an foreign key. I'm not the dogmatic type, but in this case I will not budge ;-)

TRIGGERS-Updating a value in a table not updating Oracle SQL

Table description:
two tables
Product (Prodid, Prodesc, Price, Stock,Reord)
Sales (Salesid, Proid, qty)
Question:
Create a Trigger which reduces the stock of Product that is been inserted in sales and update the stock when purchase is made.
I made a sample trigger only for purchase but my product's stock not updating.
This is my code.
SQL> create or replace trigger updat after
2 insert or update on product for each row
3 declare
4 temp number;
5 temp1 number;
6 temp2 varchar2(5);
7 begin
8 select qty into temp from purchase;
9 select proid into temp2 from purchase;
10 select stock into temp1 from product;
11 temp1:=temp1+temp;
12 update product set stock=temp1 where prodid=temp2;
13 end;
14 /
I am weak in Triggers. Help.
This trigger is a bit of a mess so it's hard to know quite where to start. I'll make a number of guesses and assumptions-- I'll try to call them out but I'll probably miss one or two.
First, the trigger likely needs to be defined on the sales table. You want to modify the product table in response to a change to the sales table. Second, the requirement only appears to require an after insert trigger, it doesn't appear that you need to modify the product table when a row in sales is updated.
CREATE OR REPLACE TRIGGER trigger_name
AFTER INSERT ON sales
FOR EACH ROW
Second, your variables are named exceptionally poorly. Calling things temp, temp1, and temp2 makes your code much harder to read and to write because you'll have no idea what any variables actually contain. That makes it much harder to read the code and see what's going on let alone notice any bugs. Always choose meaningful names.
Third, if you are going to write select into statements, you need to ensure that you are always selecting a single row. That almost certainly requires a WHERE clause, most likely using one or more columns from the :new pseudorecord to determine which row you are interested in. In this case, though, there doesn't appear to be a need to declare any local variables or to write any select statements. It sounds like you just want to
UPDATE product
SET stock = stock - :new.qty
WHERE prodID = :new.prodID
If you want to enhance your trigger to handle updates to sales in addition to inserts, you would want to compute the difference between the :old.qty and the :new.qty and subtract that from the stock of the product.
I am not sure why you are adding your qty with prodid. And the relation between the tables sales,product and purchase is not clear. From your description i think this would help.
create or replace trigger TRIG_STOCK
after insert or update on Sales
for each row
pragma autonomous_transaction;
begin
update product set stock=stock-:new.qty where prodid=:new.prodid;
update purchase set qty=:new.qty where prodid=:new.prodid;
commit;
end;

Equivalent of C# 'readonly' for an MS SQL column?

Imagine there is a Price column in Products table, and the price may change.
I'm fine with it changing but I want to store the original Price value in another column.
Is there any automatic way MS SQL server may do this?
Can I do this using Default Value field?
Do I have to declare a trigger?
Update
I tried to use Price to simplify the question but it looks like this provoked "use separate table" type of answers.
I'm sorry for the confusion I caused.
In the real world, I need to store a foreign key ID and I'm 100% I only need current and original values.
Update 2
I got a little confused by the different approaches suggested so please let me explain the situation again.
Imaginary Products table has three fields: ID, Price and OriginalPrice.
I want to setOriginalPrice to Price value on any insert.
Sometimes it is a single product that gets created from code. Sometimes there are thousands of products created by a single insert from a stored procedure so I want to handle these properly as well.
Once OriginalPrice has been set, I never intend to update it.
Hope my question is clearer now.
Thanks for your effort.
Final Update
I want to thank everyone, particularly #gbn, for their help.
Although I posted my own answer, it is largely based on #gbn's answer and his further suggestions. His answer is also more complete, therefore I mark it as correct.
After your update, let's assume you have only old and new values.
Let's ignore if the same update happens in quick succession because of a client-code bug and that you aren't interested in history (other answers)
You can use a trigger or a stored procedure.
Personally, I'd use a stored proc to provide a basic bit of control. And then no direct UPDATE permissions are needed, which means you have read only unless via your code.
CREATE PROC etc
...
UPDATE
MyTable
SET
OldPrice = Price,
Price = #NewPrice,
UpdatedBy = (variable or default)
UpdatedWhen = DEFAULT --you have a DEFAULT right?
WHERE
PKCol = #SomeID
AND --provide some modicum of logic to trap useless updates
Price <> #NewPrice;
A trigger would be similar but you need to have a JOIN with the INSERTED and DELETED tables
What if someone updates OldPrice directly?
UPDATE
T
SET
OldPrice = D.Price
FROM
Mytable T
JOIN
INSERTED I ON T.PKCol = I.PKCol
JOIN
DELETED D ON T.PKCol = D.PKCol
WHERE
T.Price <> I.Price;
Now do you see why you got jumped on...?
After question edit, for INSERT only
UPDATE
T
SET
OriginalPrice = I.Price
FROM
Mytable T
JOIN
INSERTED I ON T.PKCol = I.PKCol
But if all INSERTs happen via stored procedure I'd set it there though....
There is no readonly attribute for a SQL Server table column. BUT you could implement the functionality you describe using a trigger (and restricting permissions)
Except, it is not the best way to solve the problem. Instead treat the price as Type 2 'slowly changing dimension'. This involves having a 'ValidTo' column (os 'StartDate' and 'EndDate' columns), and closing off a record:
Supplier_Key Supplier_Code Supplier_Name Supplier_State Start_Date End_Date
123 ABC Acme Supply Co CA 01-Jan-2000 21-Dec-2004
124 ABC Acme Supply Co IL 22-Dec-2004
If you do go the route of a trigger (I suggest you use SCD type 2), make sure it can handle multiple rows: Multirow Considerations for DML Triggers
I would recommend storing your price in a seperate table called Prices, with the columns Price and Date.
Then whenever the price is updated, INSERT a new record into the Prices table. Then when you need to know the current price, you can pull from there.
However, if you wish to update an OriginalPrice column automatically, you could add a TRIGGER to the table to do this:
http://msdn.microsoft.com/en-us/library/aa258254%28v=sql.80%29.aspx
This is what I ended up with, with a heavy help from #gbn, #Mitch and #Curt:
create trigger TRG_Products_Price_I --common-ish naming style
on dbo.Products after insert as
begin
set nocount on
update m
set OriginalPrice = i.Price
from Products p
join inserted i on p.ID = i.ID
end
I tried to follow this article as well.
Thanks to everyone!