How to keep the updated rows for a period of time - sql

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

Related

Insert data with specific condition using Oracle procedure?

I want to insert data from view to table by using Oracle Procedure.
The view is called VW_INVPART. The view consist of column from different tables:
(M_Product table)
AD_ORG_ID,
AD_Client_ID,
Name,
M_Product_ID,
(M_Storage table)
QtyOnHand,
(M_Replenish table)
level_min
(M_Product_PO table)
order_min
The table I want to insert is M_RequisitionLine.
My scenario is there's goods quantity which defined in QtyOnHand. QtyOnHand is dynamic so it can be changed depends on the logistic in-out process. then there's minimum level which is defined in level_min. When goods run out of stock, we can order it again and there's minimum order quantity which is defined in order_min.
So, when the amount of QtyOnHand is less than level_min, we can add data to column Qty in M_RequisitionLine in order to request stock. But there are minimum quantity to be put in M_RequisitionLine.Qty (order_min).
If level_min - QtyOnHand <= Order_min, then set M_RequisitionLine.Qty to Order_min.
But, If level_min - QtyOnHand >= Order_min, then set M_RequisitionLine.Qty into the difference between level_min and QtyOnHand.
How can I make the procedure in the Oracle? I've tried arranged the code but still confused as I am newbie in Oracle SQL.
You could write this into a PL/SQL stored procedure in the following way by using case command. Very basic example below. Keep in mind, I don't know what the entity relationships are for your tables or what their cadinality is, so a lot of what I did is based on singular value assumptions. If the select data returns more than one row, you have to use oracle collections.
Declare
t M_Storage.QtyOnHand%TYPE;
v M_Replenish.Level_min%TYPE;
o M_Product_PO.order_min%TYPE;
Begin
SELECT QtyOnHand INTO t FROM M_Storage;
SELECT Level_min INTO v FROM M_Replenish;
SELECT Order_min INTO o FROM M_Product_PO;
CASE
When t-v < o Then UPDATE M_RequisitionLine set qty = o;
When t-v >= o then UPDATE M_RequisitionLine set qty = t-v;
END CASE;
END;
/
This functionality already exists in standard Adempiere.
For a product, define the min, max & replenishment rules on the Replenish tab of the Product Window. On the Purchasing tab, of the same window, you can define, per Supplier the Minimum Order Qty.
Now, if you run the Replenishment Report under the Material Management menu, you can run the report and generate Purchase Orders to fulfill the replenishment based on the rules you defined and this will honour the minimum order quantity set on the Purchasing tab,
It's not a technical solution but it works.
Now if your needs were even more complex fulfilment method that the standard you could define a custom replenishment rule. This is a simple java class that must implement the Interface org.compiere.util.ReplenishInterface and requires that you implement one function... getQtyToOrder.
Once you have your class defined you specify this class on the Warehouse window.
Now, on the Product window->Replenish tab you can select Custom as the Replenish Type and this class will be used to calculate the quantity to replenish.
This approach would be better as it maintains your ability to use any supported database rather than just Oracle.

How can I do loop within a loop in SQL, similar to a for-each loop?

I have pored over numerous questions and answers but can't seem to find a solution that works for me. Here is what I am trying to do:
Say I have table called "Store" that holds Store IDs, store names, and the annual sales of that store. The business wants each store to increase it's sales by 50% over the next year, and wants to calculate monthly target sales amounts that each store needs to reach to make the 50% increase value. For example, if store number 5 had $1000 in sales last year, the target for the end of the next year would be $1500. And to reach that, the amount for each month would need to be something like $1042, $1084, $1126... $1500.
What I am trying to do is insert 12 records for each store into a 'monthly plan fact' table. I am trying to select a store from the Store table, grab the annual sales amount, then do a loop inside that where I calculate what each month value would be so I can insert it into the 'monthly plan fact' table, then select the next store from the store table... and so on. As a C# developer, it seems a very simple task that could be accomplished with a 'for each' style loop with another 'for' loop inside it.
But I cannot for the life of me figure out how to do this in SQL. I know about temp tables, but I can't seem to figure out how to get multiple rows inserted into my 'monthly plan fact' table using the records from the Store table, and the values calculated in the loop to determine the monthly plan values. I've read some on cursors, but it seems that most people advise against them in the SQL community, so I am at a loss on this one.
The T-SQL below shows how to really easily get the sales targets for each store for each month. I've included two columns here: one called 'salesTargetYourExample' where we have a pattern like 1042, 1084, ... for sales targets and one called 'linearGrowthSales' where we have the new annual sales target (e.g. 1500 for the store that made 1000 last year) and simply look at what sales should be at the end of each month to reach that target assuming linear growth).
The short answer though is to always think in terms of sets and set based operations when working with databases. Don't think in terms of loops
-- Create the store table
create table dbo.Store
(
storeId int not null primary key clustered,
storeName varchar(100) not null,
annualSales money not null
);
-- Populate the data in the store table
insert into dbo.Store(storeId,storeName,annualSales)
values (1, 'My first store', 2000),
(5, 'Store number five', 1000),
(6, 'The sixth store', 2500);
-- Get the sales targets for each store on a monthly basis
select s.storeId, s.storeName, months.mth,
(s.annualSales * 0.5 * months.mth/12) + s.annualSales as salesTargetYourExample,
s.annualSales * 1.5 * months.mth/12 as linearGrowthSales
from dbo.Store as s
cross apply
(
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
) as months(mth)
In Sql you really should try to avoid using loops. Instead, you should use a set-based approach. This post (first answer) has a good link on why you shouldn't do it, then an example that could help you do it. Take a look at using a CURSOR instead.
#paqogomez was right :)
In the event you choose to do a loop in SQL though its frowned upon but it does exist. Here is how its done. This is a more general answer about loops in SQL not specific to this question. Im hoping someone who is stuck and for whatever reason cant go the other way (set based) can benefit:
Declare #intCount as int
Declare #numberOfTimes as int
WHILE #intCount <= #numberoftimes
BEGIN
--Do something repetitive
END

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;

Why Is SQL Trigger Not Inserting Rows In Sequential Order?

Recently I inherited a new ASP web application that merely allows customers to pay their outstanding invoices online. The application was poorly designed and did not have a payment history table. The entire payments table was deleted by the web service that transports the payment records to the accounting system of record.
I just created a simple trigger on the Payments table that simply copies the data from the Payments table into a Payment_Log table. Initially, the trigger just did a select * on inserted to copy the data. However, I just modified the trigger to insert the date of the payment into the Payment_Log table since one of our customers is having some issues that I need to debug. The new trigger is below. My question is that I have noticed that with this new version of the trigger, the rows are being inserted into the middle of the table (i.e. not at the end). Can someone explain why this is happening?
ALTER trigger [dbo].[PaymentHistory] on [dbo].[Payments]
for insert as
Declare #InvoiceNo nvarchar(255),
#CustomerName nvarchar(255),
#PaymentAmount float,
#PaymentRefNumber nvarchar(255),
#BulkPaid bit,
#PaymentType nvarchar(255),
#PaymentDate datetime
Select #InvoiceNo = InvoiceNo,
#CustomerName = CustomerName,
#PaymentAmount = PaymentAmount,
#PaymentRefNumber = PaymentRefNumber,
#BulkPaid = BulkPaid,
#PaymentType = PaymentType
from inserted
Set #PaymentDate = GETDATE()
Insert into Payment_Log
values (#InvoiceNo, #CustomerName, #PaymentAmount, #PaymentRefNumber, #BulkPaid, #PaymentType, #PaymentDate)
Below is a screenshot of SQL Server Management Studio that shows the rows being inserted into the middle of the table data. Thanks in advance for the help guys.
Datasets don't have an order. This means that SELECT * FROM x can return the results in a different order every time.
The only time that data is guaranteed to come back in the same order is when you specify an ORDER BY clause.
That said, there are circumstances that make the data normally come back in a certain order. The most visible one is with a clustered index.
This makes me wonder if the two tables have a Primary Key or not. Check all the indexes on each table and, at the very least, enforce a Primary Key.
As an aside, triggers in SQL Server are not fired for each row, but each batch. This can mean that the inserted table can contain more than just one row. (For example, when bulk inserting test data, or re-loading a large batch of transactions.)
For this reason, copying the data into variable is not a standard practice. Instead, you could just do the following...
ALTER trigger [dbo].[PaymentHistory] on [dbo].[Payments]
for insert as
INSERT INTO
Payment_Log
SELECT
InvoiceNo, CustomerName, PaymentAmount, PaymentRefNumber,
BulkPaid, PaymentType, GetDate()
FROM
inserted
Ok This question has the same answer as why do rows come back in different orders when I do not use an order by clause in my SQL query. If you ask SQL to process rows in any way it will process them in the fastest way it can, cashed rows first, those nearest the read head on the hard drive next and finally the rest.
Put it another way: you would be annoyed if queries took 10 times longer with rows in order than just on a first come first served basis. SQL does what you ask as quickly as it can
Hope this helps

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!