TRIGGERS-Updating a value in a table not updating Oracle SQL - 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;

Related

SQL query to keep track of new orders or balance changes in old orders without dates

I have a table with records similar to the following
order_id
order_balance
34
400
35
200
36
100
Once an order_id is inserted i.e. order_id = 34. A new record is not inserted(not an append) when the order_balance changes, the balance itself is updated in the same record. So say the balance for order_id=34 changes tomorrow to 300. When you look at the table tomorrow.
the record for order_id = 34 will look like:
order_id
order_balance
34
300
So I want to build a table that keeps track of the order_ids and the order_balances that are either newly inserted, or have had the balance change in the last day(or time period, could be hours,days,minutes,etc). I am using pyspark with spark sql.
My first thought is to have a table that keeps track of the balances from yesterday and then compare them with the balances today. However this would be two separate runs of the spark job and would require me to have the table persist between job runs in spark. Is this even possible?
Have you tried a TRIGGER ON AFTER INSERT/UPDATE? It would then insert or update the new "control_table" according to the rules you want.
Or, if I understood it correctly, you could have a VIEW like this:
CREATE VIEW v_changed_orders_last_day AS
SELECT
order_id,
order_balance,
order_updated_date
FROM orders
WHERE order_updated_date IS NULL
OR order_updated_date >= sysdate()-1;
You want to track changes... convert the table into a System-Versioned Temporal Table.
Now SQL Server will track every change made to that table by creating a log table for it.
You would have to create a new table for control and use a TRIGGER. Below an example for ORACLE. The syntax can vary depending on the database:
CREATE OR REPLACE TRIGGER my_trigger
AFTER INSERT OR UPDATE
ON original_table
FOR EACH ROW
BEGIN
-- Insert record into control table
INSERT INTO control_table
( order_id,
old_order_balance,
new_order_balance,
date )
VALUES
( :new.order_id,
:old.order_balance, --gets the old value for order balance
:new.order_balance, --gets the new value for order balance
sysdate() --gets the timestamp when it occurred
);
END;

How to keep the updated rows for a period of time

I am working with small project using SQL Server, and I need some help from you, to clarify the problems I am having with a task.
What I am trying to do is update the row in table and keep these updated rows for a period of time.
For example I am using this code to update:
ALTER PROCEDURE [dbo].[updtprice]
#bc int,
#price float
AS
UPDATE tblproduct
SET Price = #price
WHERE Barcode = #bc
What I am trying to do is, for example I update the price of the product with barcode 2233 from 0.99 cent to 0.70 (today). And I want the price to be reverted to old one after one or two week( certain data set by user).
How can I accomplish this task?
Thanks to everyone
There may be other ways to do it but it sounds like you need another table that records the previous value and date that it expires. Then you would have a "job" that runs daily that takes note of any expired and does the update back to previous value.
OR you have additional columns in this table with the special price and expiration date and any applications that use that row look at the special price, is it still good, if so use it, if not use the regular price.
To be honest, the only way to properly do this is either:
Write a script that does this.
Write a scheduled task in MS SQL.
Both not great ways to do it in all honesty. I would personally go with option 1 if it's a web-application and option 2 if it's just a Database.
you can have 3 columns in your table.
actual_price
current_price
price_update_date
actual_price columns will always have exact price of the product.
you can update the current_price with the discounted price and also update the strong text price_update_date column when updating the current_price.
like.
ALTER procedure [dbo].[updtprice]
#bc int,
#price float
as
update tblproduct set current_price= #price,
price_update_date=SYSDATETIME()
where
Barcode = #bc
and then you can create another stored Proc which will run everyday at 12 am.
and chech if current_time -price_update_date is greater than or equal to your threshold then update the current_price of the product with actual_price

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

Making a sequence of SQL statements atomic

I need to make the following sequence of steps in a sproc atomic. Here is an approximate simplified example:
Customesrs table (CustomerId,..,OrderMax)
Products table (ProductId,...)
AvailableProducts view (ProductId and other properties)
Orders (CustomerId,OrderId)
select #OrderMax from the Customers table
select TOP #Ordermax from AvailableProducts view
update some properties in the Products based on the result set of step 2
insert orders into Orders table (based on the result set of step 2)
return/select orders that were inserted
As I understand, there has to be a transaction on the whole thing and UPDLOCK. What has to be secured is Products table for update and Orders table for insert. However, the rows are queried from the view that is constructed from both of these tables.
What is the right way to make this sequence atomic and secure update and insert on the above tables?
Need to wrap all you logic in a Begin Transaction, Commit Transaction. The update / insert does not really care if the data came from a join unless it somehow created a situation where it could not roll back the transaction but it would have to get real messy to create a situation like that. If 3. and 4. have complex logic you may be forced into a cursor or .NET (but you can do some pretty complex logic with regular queries).