Oracle database, conditional column - sql

I'm not sure from where to start hitting this problem from.
I have huge table, at least hundreds of thousand rows.
One of the columns should be price, but in customers currency. The data available is
Price data in EUR
Package with function to get exchange rate
Of course, exchange rates will update frequently and first fetching the row, then checking if customer uses EUR and if not, doing another fetch for rate information seems like a bad idea.
Also many of the customers will use EUR, so always checking the rate with same query might cause useless overhead.
My first idea was to fetch price and customer data (both needed anyways) and then if customer uses different currency from EUR, I would make another query.
Second idea was to make table witch would have correct price in customer currency to start with. Maybe using Oracle function (which again I already have)
I think, second way is 'more correct' implementation but I have no idea how to use package function to get one column in view.
Is there better way, or should i go with one of these?

As you said that exchange rates are updated frequently and you will always need updated currency values so My suggestion is to create the VIEW and use it as a table wherever you need in your application.
create or replace view your_view_name as
select <column_list>,
case when currency = 'EUR' then price else price*conversion_rate end as price
from <both of your tables with join>
Cheers!!

Creating a view is not a bad call, but you have to realise it's actually just a header, so the query in there would execute each time the value is called, so I'm not sure that's exactly what you want.
what you can do is create a materialized view for all values in all currencies using the function, index it properly for fast search and refresh every time your rates change (doubt they will change every 15 mins) - for materialized view the refresh will perform automatically if set this way.

Virtual column comes to your rescue. Note that virtual columns are there starting with Oracle 11g.
Simple add the formula defining the calculation as an virtual column as in example below.
alter table price add
( home_price NUMBER GENERATED ALWAYS AS
(case when currency = 'USD' then price
else price * convert_rate('EUR','USD') end) VIRTUAL
);
Note that is is required, that the rate conversion function is declated as DETERMINISTIC.
Here is the full example for converting EUR in the home rate of UDS
create table price as
select 10 price, 'EUR' currency from dual union all
select 20 price, 'USD' currency from dual;
create or replace function convert_rate (p_from varchar2, p_to VARCHAR2) return number DETERMINISTIC as
BEGIN
return 1.1;
END;
/
alter table price add
( home_price NUMBER GENERATED ALWAYS AS
(case when currency = 'USD' then price
else price * convert_rate('EUR','USD') end) VIRTUAL
);
select *
from price;
PRICE CUR HOME_PRICE
---------- --- ----------
10 EUR 11
20 USD 20

Related

Combining 2 Tables with the"Same" Primary Key in a function

i gotta write a function for my database to get the price in the order table from my Article table. I Got 2 tables for articles, 1 for rent items and 1 for sale Items.
Both tables use the Primary Key ItemNo, Sales starting at a ItemNo of 1 and Rent starts with itemNo >=1000.
I wrote the function down below for the table sales and it does also work. I am just not sure how to combine the 2 functions right now to get it from both tables.
Should I use an if-case and wrote the function with if inItemNo>=1000 then function for Rent and else Function Sales or should I use an Join? if I should use a join, I am not quite sure how to use it correctly. May someone can help me.
thanks in advance
DELIMITER $$
create or replace function fn_PurchasingPrice(inItemNo int) returns int
begin
declare OutPurchasingPrice int;
set OutPurchasingPrice=(select ItemNo
from Sales
where ItemNo= inItemNo);
return ifnull(OutPurchasingPrice,-1);
END$$
DELIMITER ;
I think we have a database design issue. Here are some options:
What I understand is that you want a function that simply returns that price of something, a rent or sale item.
These items have different itemNo's (PK) stored in their own tables. This implies that they are different entities. The fact that they share a price field, doesn't mean they are related. This means you might want to either:
Treat them separately and have two functions that return the price for each fn_SalePurchasingPrice and fn_RentPurchasingPrice.
Create a relationship.
If you really don't want a relationship nor two separate function, which I wouldn't really recommend, you can pass 2 PK values into the function, one for each table. You can use it like this: fnPurchasingPrice(6011, 3048) and it would return two values using a union.
select price from table1 where table1_id = value1
union select price from table2 where table2_id = value2
The number of items in each table can exceed 1000 and so the fact that one table's PK starts at 1000 is not useful.

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

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.

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;