Comparing day and week prices and getting cheapest - mysql

I'm trying to get the lowest price of an object. Problem is there can be daily and weekly prices. So when searching for the cheapest price i have to multiply the day price time 7 and compare to the week price to get the cheapest.
It can also happen that an object has week prices only or day prices only (or no prices at all).
BTW: It has to be such a subselect query, cause i have some more WHERE queries following later.
Pricetable
id price type oid
1 10 d 1
2 12 d 2
3 70 w 1
4 80 w 2
Objects
id name
1 house1
2 house2
This is what i'm using but its not working correctly. When the day price*7 is bigger that the week price it still gives me the day price.
SELECT p.oid, p.price, p.id, p.type FROM Pricetable p INNER JOIN (
SELECT oid, MIN(IF(type="w",price, price*7)) AS price, id, type
FROM Pricetable
GROUP BY oid
) p2 ON p.oid = p2.oid AND p.id= p2.id

Your query should work to get the minimum price. However, it should be written as:
SELECT oid, MIN(CASE WHEN type = 'w' THEN price ELSE 7*price END) AS price
FROM Pricetable
GROUP BY oid ;
If you want other values from row with the minimum price, then you need more logic. How about this?
SELECT pt.*oid, MIN(CASE WHEN type = 'w' THEN price ELSE 7*price END) AS price
FROM Pricetable pt
WHERE pt.id = (SELECT pt2.id
FROM PriceTable pt2
WHERE pt2.oid = pt.oid
ORDER BY (CASE WHEN pt2.type = 'w' THEN pt2.price ELSE 7*pt2.price END)
LIMIT 1
);

You'll need a sub-select with some sort of ranking to get the cheapest price.
Check https://dba.stackexchange.com/questions/13703/get-the-rank-of-a-user-in-a-score-table That should get you on the right track.
I don't have access to MySQL right now, and it's a bit different than MSSQL. But that link should set you on the path.

Related

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.

MySQL, select to include earlier weeks

I'm using the SQL below to get the price history per week from two MySQL database tables:
SELECT ROUND(MIN(ph.price), 0) AS min_price, WEEK(ph.added) AS added
FROM products_data pd JOIN products_history ph ON pd.id = ph.product_id
WHERE ph.price > 0
GROUP BY added
ORDER BY added
Everything is good, except the products_history table only contains entries when a price is changed.
If a week A has a price change that creates the lowest min_price in week A, everything is good.
But if the price change happened 1+ weeks before week A, it's not detected by the query and the retrieved lowest min_price for week A may therefore not correct :(
Example, what is happening right now:
Week 13: Price change so min_price for product A = 4.78,
query returns 4.78 (correct)
Week 14: Price change so min_price for product B = 4.74,
query returns 4.74 (correct)
Week 15: Price change so min_price for product C = 4.79,
query returns 4.79 (wrong, should be 4.74 since that's still the price for product B)
Week 16: Price change so min_price for product C = 4.75,
query returns 4.75 (wrong, should be 4.74 since that's still the price for product B)
Week 17: No price changes,
query returns nothing... (wrong, should be 4.74 since that's still the price for product B)
Week 18: Price change so min_price for product B = 4.77,
query returns 4.77 (wrong, should be 4.75 since that's still the price for product C)
Any ideas for how to make the query retrieve the lowest min_price per week even if the change happend earlier?
products_data has these columns:
id, name, category
2434, 'Product A', 'Bits'
3437, 'Product B', 'Bits'
products_history has these columns:
id, product_id, price, added
4311, 2434, 4.74, 2019-05-15 22:19:50
2434, 3437, 4.78, 2019-05-15 22:19:59
Working version based on Gordon Linoff's correct answer below:
SELECT id, yw.yyyyww,
(SELECT ph2.price
FROM products_history ph2
WHERE ph2.price > 0 AND
YEARWEEK(ph2.added) <= yw.yyyyww AND ph2.product_id = pd.id
ORDER BY YEARWEEK(ph2.added) DESC, -- put the most recent values first
ph2.price ASC -- by lowest price
LIMIT 1
) AS min_price
FROM products_data pd CROSS JOIN
(SELECT DISTINCT YEARWEEK(ph.added) AS yyyyww
FROM products_history ph
) yw
If I understand correctly, you want a value for every product for every week. If so, use cross join to generate the rows and then -- in MySQL -- you can use a correlated subquery to get the price
SELECT product_id, yw.yyyyww,
(SELECT ph2.price
FROM products_history ph2
WHERE ph2.price > 0 AND
YEARWEEK(ph2.added) <= yw.yyyyww
ORDER BY YEARWEEK(ph2.added) DESC, -- put the most recent values first
ph2.price ASC -- by lowest price
) as min_price
FROM products_data pd CROSS JOIN
(SELECT DISTINCT YEARWEEK(ph.added) as yyyyww
FROM products_history ph
) yw

How to query historical prices where there are no initial dates nor end dates?

I'm trying to make a SQL query to find the product that has raised the least amount of money, but I'm facing a problem, and it is that I have a table which keeps track of different dates for prices, for example Product 1 has a price of 5000 for all the recipts purchased between 2011-01-01 and the next date of a price change and that is 2012-01-01, on the other hand Product 1 has the latest price date as 2015-01-01 and its price is 5600, so it means that any recipt purchased from 2015-01-01 to an actual date has a price of 5600.
Here is a SQL Fiddle of the current problem:
http://sqlfiddle.com/#!9/62ec25/9
And the query I did to get an aproximation to the answer:
select pr.productName, sum(d.quantity*p.price)
from details d, product pr, price p, recipts r
where d.Product_ref=pr.ref
and pr.ref=p.Product_ref
and r.numRecipt=d.Recipts_numRecipt
group by pr.productName
order by 2 asc;
I'm pretty sure the query is wrong because I'm not taking into consideration the historical price for each product, but I cannot find a way to make use of the "in between" SQL operator, How can I get the expected result?
I think you'd want to get your price table into a structure that looks like:
| product_ref | price | startDate | endDate |
Then build a query that joins on that table where the purchase date is between the price date range:
SELECT
pr.productName,
SUM(d.quantity * p.price)
FROM
details d
INNER JOIN product pr ON d.product_ref = pr.ref
INNER JOIN
(
SELECT
p.product_ref,
p.price,
MIN(p.priceDate) AS StartDate,
MIN(p1.priceDate) AS EndDate
FROM price p
LEFT JOIN price p1 ON p.product_ref = p1.product_ref
WHERE
p.priceDate < p1.priceDate
GROUP BY p.product_ref, p.price
) p ON pr.ref = p.product_ref
INNER JOIN recipts r ON r.numRecipt = d.recipts_numRecipt
WHERE
r.dateOfPurchase >= p.startDate AND r.dateOfPurchase < p.endDate
GROUP BY pr.productName

Calculating the sum of quantity * unit price in mysql

I think I'm missing a simple step here but I can't seem to figure it out. I've read the other threads and they talk about grouping but I can't seem to put it all together right.
I have a simple table that holds inventory transactions. In each row, there is a quantity and a price. I want to get the sum of the quantity and the sum of the each price * each quantity.
Here's my query. If I remove the grouping, I get 1 result that is multiplied by the number of rows in the table. If I add the grouping, I get the correct result multiple times. Am I missing something here? I just feel like running a query to get 20k results when they all contain the same data would be pointless.
SELECT (SUM(i.quantity) - IFNULL(SUM(s.quantity), 0)) AS quantity,
SUM(i.unitprice * i.quantity) AS totalprice
FROM 02_01_transactions t
LEFT JOIN 02_01_transactions i
ON i.type = 1
AND i.active = 1
LEFT JOIN 02_01_transactions s
ON s.type = 2
AND s.active =1
GROUP BY t.id
Not sure there is a need for the joins (you are not joining on any common value) or the type = 2 rows if you are just subtracting them out. Is there a reason the following does not work?
-- Total quantity, total price of all type 1, active transactions.
SELECT SUM(quantity) AS quantity,
SUM(unitprice * quantity) AS totalprice
FROM 02_01_transactions
WHERE type = 1
AND active = 1
Here's my guess at what you were trying to accomplish:
select
sum(quantity * case type when 1 then 1 when 2 then -1 end) as quantity,
sum(unitprice * quantity) as totalprice
from 02_01_transactions
where type in (1, 2) and active = 1

Mysql join where value or null

I've got a table PROD
ID NAME
1 Apple
2 Banana
And the relative table PRICES , with global prices (ID_USER is Null)
or per-user prices (ID_USER)
PROD_ID USER_ID PRICE
1 null 10
1 5 8
Now, i need a query that finds all products and the relative prices,
the catch is that i'm trying to retrieve the user price only if there is one, else retrieve the global price
SELECT PROD.* , PRICES.* FROM PROD
LEFT JOIN PRICES ON PROD.ID_PROD = PRICES.ID_PROD
WHERE PRICES.USER_ID IS NULL OR PRICES.USER_ID = 5
This query returns 2 rows (the prod joined with the 2 prices)
Is there a way to retrieve the exact price for the product in just one query ?
thanks !!
EDIT: In the join i need the per-user price row (the last one) only if the row exists , else i need to retrieve the row with the global price, is that possible ?
SELECT
prod.*,
COALESCE(p_user, p_default) As Price
FROM
Prod INNER JOIN (SELECT
PROD_ID,
MAX(CASE WHEN USER_ID=5 THEN Price END) p_user,
MAX(CASE WHEN USER_ID IS NULL THEN Price END) p_default
FROM
Prices
GROUP
BY PROD_ID) m_uid
ON Prod.ID = m_uid.Prod_ID
Please see fiddle here.