MySQL Query to calculate average price until stock value reaches 0 - mysql

I have two MySQL tables which I would like to use but unfortunately this question exceeds my knowledge.
First table shows the stock I have with product ID and Quantity
id PID Quantity
1 5 8
2 45 7
3 125 0
The second table stores my purchase orders I did to purchase the products I have in stock.
id Date PO ID PID Price Purchased Quant.
1 1.1.19 PO1 5 8.00 7
2 1.1.19 PO1 45 2.15 9
3 2.1.19 PO2 5 4.45 6
As a result I would like to get the average price per PID caclulated from the latest purchase date.
PID Average Price
5 5.3375
45 2.15
For PID '5' I have 8pcs in stock and I find 6 pcs purchased on 2.1.19 for 4,45€. Therefore I need to find another 2 pcs in an older purchase which I can find on 1.1.19 for 8.00€.
Average price calculation -> (6pcs * 4.45€ + 2pcs * 8.00€)/8pcs = 5.3375€
For PID 45 the calculation should be the same however my stock is completely fulfilled with the first PO.
Is there any way I can solve this in MySQL with a query.

In versions of MySQL before 8.0 this is not easy to achieve. You can do it with the use of variables that change during the execution of the query, but be aware that there is no documented guarantee that the order of variable assignment is as might be expected.
Now that I have given the disclaimer, here is the query you could use:
select pid,
sum(price * take)/sum(take) avg_price
from (
select pid,
price,
#quantity := if(#pid = pid, #quantity, available) needed,
least(#quantity+0, quantity) take,
#quantity := #quantity - least(#quantity, quantity),
#pid := pid
from (select o.*,
p.quantity available
from orders o
inner join products p
on p.pid = o.pid
order by o.pid,
o.date desc) data,
(select #pid := -1,
#quantity := 0) vars
order by pid, date desc) base
group by pid
fiddle
For a more reliable way, you would better migrate to MySQL where you can use window functions.

Related

Rolling count daily active drivers since beginning

I have a list of drivers, orders, and dates in a table named all_data between 2022-01-01 and 2022-01-15 (15 days) like this:
driver_id
order_id
order_date
1
a
2022-01-01
1
b
2022-01-02
2
c
2022-01-01
2
d
2022-01-03
For all 15 days, how do I find the number of continually active drivers, who completed at least one order every single day, up to that date? The output should be a table like this:
order_date
active_drivers
2022-01-01
30
2022-01-02
27
2022-01-03
25
For example, on 2022-01-01, there were 30 unique drivers who completed at least one order that day. On 2022-01-02, we must find the number of unique drivers who completed at least one order on 2022-01-01 and 2022-01-02. On 2022-01-03, we must count drivers who completed at least one order on 2022-01-01, 2022-01-02, and 2022-01-03.
What I have tried
I found a similar solution in MySQL (below) but it is not allowed in bigquery because of the error "Unsupported subquery with table in join predicate".
MySQL
SELECT order_date,
(SELECT COUNT(distinct s1.driver_id) as num_hackers
FROM all_data s2
join all_data s1
on s2. order_date = s1. order_date and
(SELECT COUNT(distinct s3. order_date)
FROM all_data s3
WHERE s3.driver_id = s2.driver_id
AND s3. order_date < s1. order_date)
= datediff(s1. order_date, date('2022-01-01'), day)
))
from all_data
I also read this Google BigQuery: Rolling Count Distinct question but that is for a fixed 45 number of days, while the number of days here is a variable based on the date. How do I write a query in BigQuerySQL to find the rolling number of continually active drivers per day?
Consider below
select order_date, count(distinct if(flag, driver_id, null)) active_drivers
from (
select order_date, driver_id,
row_number() over(partition by driver_id order by order_date) -
date_diff(order_date, min(order_date) over(), day) = 1 as flag
from (select distinct order_date, driver_id from all_data)
)
group by order_date
First find out all combination of dates and drivers, then just get count of all drivers per date. Try this:
select order_date, count(*)
from(
select order_date, driver_id, count(*)
from all_data ad
group by order_date, driver_id)
group by order_date

MYSQL Recursive query with conditions

I have 2 tables,
the first table contains id_product, its rate and its price.
ID_product
rate
Price
1
TSA1
0.12
2
TSA1
1.5
1
TSA2
0.14
2
TSA2
1.7
1
TSA3
NULL
2
TSA3
1.7
1
TASM4
1.68
I have an other table which contains a rate and its rate destination if the price for its rate is NULL. Its for always has a price for each product. Here for example, the product 1 doesnt have a price for the rate TSA3. The correspondance table says that if it doesnt have a price for this rate, use the price of TASM4.
Origin_rate
Destination_rate
TSA1
TAS2
TSA2
TAS3
TSA3
TASM4
So, How can I complete my first table? I think, i need a recursive query, but i dont know how to do it in SQL.
This query gets you the price hirarchically:
with recursive cte(id_product, rate, price, origin_rate) as
(
select id_product, rate, price, rate
from mytable
union all
select cte.id_product, cte.rate, t.price, t.rate
from cte
join map on map.origin_rate = cte.origin_rate
left join mytable t on t.id_product = cte.id_product
and t.rate = map.destination_rate
where cte.price is null
)
select id_product, rate, price
from cte
where price is not null
order by id_product, rate;

MySQL most price change over time

price | date | product_id
100 | 2020-09-21 | 1
400 | 2020-09-20 | 2
300 | 2020-09-20 | 3
200 | 2020-09-19 | 1
400 | 2020-09-18 | 2
I add an entry into this table every day with a product's price that day.
Now I want to get most price drops for the last week (all dates up to 2020-09-14), in this example it would only return the product_id = 1, because that's the only thing that changed.
I think I have to join the table to itself, but I'm not getting it to work.
Here's something that I wanted to return the most price changes over the last day, however it's not working.
select pt.price, pt.date, pt.product_id, (pt.price - py.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
where pt.price - py.price > 0
order by change
I understand that you want to count how many times the price of each product changed over the last 7 days.
A naive approach would use aggregation and count(distinct price) - but it fails when a product's price changes back and forth.
A safer approach is window functions: you can use lag() to retrieve the previous price, and compare it against the current price; it is then easy to aggregate and count the price changes:
select product_id, sum(price <> lag_price) cnt_price_changes
from (
select t.*, lag(price) over(partition by product_id order by date) lag_price
from mytable t
where date >= current_date - interval 7 day
) t
group by product_id
order by price_changes desc
Try using MAX() and MIN() instead....
select MAX(pt.price), MIN(pt.price), MAX(pt.price) - MIN(pt.price) as change
from prices as pt
inner join (
select *
from prices
where date > '2020-09-20 19:33:43'
) as py
on pt.product_id = py.product_id
order by change
Instead of subtracting every row by every other row to get the result, you can find the max and min's easily by means of MAX() and MIN(), and, ultimately, **MAX() - MIN()**. Relevant lines from the linked MySQL documentation...
MAX(): Returns the maximum value of expr.
MIN(): Returns the minimum value of expr.
You won't be able to pull the other fields (id's, dates) since this is a GROUP BY() implied by the MAX() and MIN(), but you should then be able to get that info by query SELECT * FROM ... WHERE price = MAX_VALUE_JUST_ACQUIRED.
This examples will get you results per WeekOfYear and WeekOfMonth regarding the lowering of the price per product.
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
WEEK(m1.ddate) AS weekofyear
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofyear,m1.product_id
ORDER BY weekofyear;
SELECT
COUNT(m1.product_id) as total,
m1.product_id,
FLOOR((DAYOFMONTH(ddate) - 1) / 7) + 1 AS weekofmonth
FROM mytest m1
WHERE m1.price < (SELECT m2.price FROM mytest m2 WHERE m2.ddate<m1.ddate AND m1.product_id=m2.product_id LIMIT 0,1)
GROUP BY weekofmonth,m1.product_id
ORDER BY weekofmonth;
Try this out in SQLFiddle.

find price difference for different date

how find difference of price for two selected day. my table as shown
---------------------------------------
id price date product
---------------------------------------
1 10 15-12-2013 pen
2 40 15-12-2013 book
3 15 16-12-2013 pen
4 42 16-12-2013 book
-------------------------------------
i want an sql query to get output like, if startdate:15-12-2013 & enddate: 16-12-2013
product startdate(15-12-2013) enddate(16-12-2013) difference
--------------------------------------------------------------
pen 10 15 5
book 40 42 2
--------------------------------------------------------------
Maybe something like this?
select
p1.product,
p1.price,
p2.price,
p1.price - p2.price as diff
from
product p1,
product p2
where
p1.product=p2.product and
date(p1.date)='2013-12-15' and
date(p2.date)='2013-12-16'
If performance is a question then this link can provide a better alternative for the date matching part: MySQL SELECT WHERE datetime matches day (and not necessarily time)
Try this:
SELECT product, StartDatePrice, EndDatePrice, (EndDatePrice - StartDatePrice) AS Difference
FROM (SELECT product, MAX(IF(a.date = '15-12-2013', a.price, 0)) AS StartDatePrice,
MAX(IF(a.date = '16-12-2013', a.price, 0)) AS EndDatePrice
FROM tableA a
GROUP BY product
) AS A;
If you insert a row on your prices table whenewer a price changes, and not every day, you should consider using this query:
SELECT
p1.product,
p1.price as stardtade,
p2.price as enddate,
p2.price-p1.price as difference
FROM
prices p1 INNER JOIN (SELECT product, MAX(dt) max_dt
FROM prices
WHERE dt<='2013-12-15'
GROUP BY product) st
ON p1.product=st.product AND p1.dt = st.max_dt
INNER JOIN
prices p2
ON p1.product=p2.product
INNER JOIN (SELECT product, MAX(dt) max_dt
FROM prices
WHERE dt<='2013-12-16'
GROUP BY product) ed
ON p2.product=ed.product AND p2.dt = ed.max_dt
it is more complicated, but it will work even if some dates are not present in your table. In that case it will use the lask known value for the price.
Please see fiddle here.

Mysql query statement to find how many time I am the max amount for a listing

Here is my tabel structure.
id
veh_id
user_id
amount
...
I have other tables to relate the user_id and veh_id as well.
I want to know how many times a user has put an amount on each veh_id and on how many occasions, this amount is actually the highest amount received. I would like to have those 2 counts for each user available.
id, veh_id, user_id, amount
1 1 30 100
2 1 32 105
3 2 30 100
4 2 32 95
5 2 33 90
I would like the select statement to give me:
user 30 as bid 2 times and 1 time is the higest bidder
user 32 as bid 2 time ans 1 time is the higest bidder
user 33 bid 1 time and 0 time the highest bidder
I don't know if it is possible to get those numbers.
This might be close, not sure exactly how you're relating vehicles together.
select
user_id,
count(*) as num_bids,
SUM(is_highest) as max_bids
from ( select
a.user_id,
COALESCE((select
MAX(b.amount) < a.amount
from bid as b
where b.id < a.id
and b.veh_id=a.veh_id
), 1) as is_highest
from bid as a
) as c
group by user_id
My understanding is user 30 has 2 max bids (2 first bids on a vehicle).
EDIT: If you're just looking for total 1 max bid per vehicle, let me know. That's actually a lot easier than rolling back to see who's bids were max when they came in...
EDIT2: Solution for only 1 max counts per vehicle:
Seems like this should be simpler for some reason:
select
user_id,
count(*) as num_bids,
count(vamt) as num_max
from bid
left join (
select veh_id as vid, max(amount) as vamt
from bid
group by veh_id
) as a on vid = veh_id and vamt <= amount
group by user_id
Try this,
select x.user_id, x.bid_times, COALESCE(y.max_times,0) as max_times from
(select user_id, count(*) as bid_times from testt group by user_id) as x
LEFT JOIN
(select user_id, count(*) as max_times from testt a where 0=( select count(*) from testt where amount > a.amount and veh_id=a.veh_id ) group by user_id) as y
ON x.user_id=y.user_id