Get last fixed price per product from table - mysql

It seems to be an easy question by SQL, but I can't get it done in MySQL.
I have a table with prices for different products (ProdID) valid from certain dates and related from the quantity. For the current price list I need to get the price valid since the latest valid_from date in the table. The timestamp cannot be used as sometimes the prices inserted are for future valid_from dates, same with the ID, with is not representative to the actual prices.
ID, ProdID, qty, price, valid_from, timestamp
100 51 25 3.360 2021-02-15 2021-05-11 19:20:28
101 51 2000 3.150 2021-02-15 2021-05-11 19:20:29
102 51 6000 2.930 2021-02-15 2021-05-11 19:20:30
103 51 15000 2.870 2021-02-15 2021-05-11 19:20:31
131 51 1000 3.250 2021-02-15 2021-05-11 19:20:59
....
140 51 25 3.970 2021-10-06 2021-10-06 16:51:48
141 51 1000 3.790 2021-10-06 2021-10-06 16:51:50
142 51 2000 3.650 2021-10-06 2021-10-06 17:45:49
143 51 6000 3.500 2021-10-06 2021-10-06 16:51:54
144 51 15000 3.400 2021-10-06 2021-10-06 16:51:56
For example, these are the rows for ProdID 51.
I need to get the prices which are currently valid. In this case the ID 141 to 144 but this is only coincidentally. Also prices may be reduced so I can't go for the highest prices per prodID. The only criteria is the latest valid_from date which is <= date(). As mentioned there could be also some already inserted prices for the future (> date()).
I tried this, but it brings all above rows, valid from 2021-05-11 AND those valid from 2021-10-06:
SELECT `p`.`qty` AS `quantity`,
`p`.`price` AS `price`,
`p`.`ProdID` AS `ProdID`,
row_number() OVER (PARTITION BY `p`.`ProdID`
ORDER BY `p`.`valid_from` DESC,`p`.`qty`) AS `rk`
FROM `tblprices` `p`

You can use window functions. Use RANK to find greatest row(s) per group:
WITH cte AS (
SELECT *, RANK() OVER (PARTITION BY ProdID ORDER BY valid_from DESC) AS rnk
FROM t
WHERE valid_from <= CURRENT_DATE
)
SELECT *
FROM cte
WHERE rnk = 1

Related

MySQL 5.7 compare row to previous row

I have a table containing daily sales. Not every day has sales (so there are 'missing' rows in the table).
I'm using MySQL 5.7, so there are no window functions available.
The structure of the table is date(timestamp), sales volume
date
sales volume
+/- prev DAY
2022-10-18
76
0
2022-10-17
131
55
2022-10-16
110
-21
2022-10-15
102
-8
2022-10-14
201
99
2022-10-10
100
-101
As an example, sales on 14-10 were 201, which were 99 more than sales for the previous row (15/10, 102)
I wish to derive the value in the 3rd column, comparing sales for the particular day, with those of the previous row (which isn't always the previous day), but can't seem to get anything working.
Thanks.
SELECT `sales volume` - #previous `+/- prev DAY`,
`date`,
#previous := `sales volume` `sales volume`
FROM test
CROSS JOIN (SELECT #previous := NULL) init_variable
ORDER BY `date` DESC
+/- prev DAY
date
sales volume
null
2022-10-18
76
55
2022-10-17
131
-21
2022-10-16
110
-8
2022-10-15
102
99
2022-10-14
201
-101
2022-10-10
100
fiddle
The expressions ordering in the output list and ORDER BY expression are critical. If you want to reorder output columns and/or output rows then use this query as subquery and set needed columns order and/or rows ordering in outer query.

Query runs too slow and even it stops because exceded of time with 17000 rows

I have table 1:
historial_id
timestamp
address
value
insertion_time
1
2022-01-29
1
84
2022-01-31
2
2022-01-29
2
40
2022-01-31
3
2022-01-30
1
84
2022-01-31
4
2022-01-30
2
41
2022-01-31
5
2022-01-30
2
41
2022-01-31
(sometimes it has repeated rows)
...
I need a Query to get:
timestamp
value(address 1)
value(address 2)
2022-01-29
84
40
2022-01-30
84
41
......
I tried with:
SELECT timestamp, ( SELECT value
FROM historical
WHERE register_type=11
AND address=2
AND timestamp=t1.timestamp
GROUP BY value
) AS CORRIENTE_mA,
( SELECT value
FROM historical
WHERE register_type=11
AND address=1
AND timestamp=t1.timestamp
GROUP BY value ) AS Q_M3pH
FROM historical AS t1
GROUP BY timestamp;
But it's too slow, it even stops because of exceeded time.
I tried with distinct too instead of group by
I think you need dynamic pivot.
Please try and avoid MySQL reserved words like timestamp.
Below query return only the max value for address 1 and 2 grouping by timestamp.
This is a simplified version of your query :
select
`timestamp`
, max(case when address=1 then value end) as value_address1
, max(case when address=2 then value end) as value_address2
from historical
group by `timestamp`;
Result:
timestamp value_address1 value_address2
2022-01-29 84 40
2022-01-30 84 41
Demo

Finding the largest price increase/decrease in a MySQL table

I am trying to find a way to get the largest price difference (in a time frame, e.g. 24 hours) in a MySQL table using a source and productId as reference.
Here is a sample product, productId 22.
id price createdAt updatedAt sourceId productId
21 799.00 2017-07-26 19:46:46 2017-07-26 19:46:45 1 22
853 920.00 2017-07-26 06:46:46 2017-07-26 06:46:46 1 22
855 799.00 2017-07-22 16:17:11 2017-07-22 16:17:11 2 22
851 770.00 2017-07-21 16:17:11 2017-07-21 16:17:11 1 22
856 799.00 2017-07-20 16:17:11 2017-07-20 16:17:11 2 22
852 599.00 2017-07-19 16:17:11 2017-07-19 16:17:11 1 22
857 810.00 2017-07-18 16:17:11 2017-07-18 16:17:11 2 22
858 799.00 2017-07-17 16:17:11 2017-07-17 16:17:11 2 22
In the example above for productId 22 I am sorting by createdAt, so in this scenario I'd take id 21 and substract it from id 853, this would give -121, meaning the product went down 121 dollars.
In the full data it's a mush up of prices, sourceIds and productIds. The goal here is to make a result look like this:
id createdAt sourceId productId adjustment
21 2017-07-26 19:46:46 1 22 -121
22 2017-07-26 16:46:46 2 22 201
23 2017-07-26 15:46:46 6 24 -20
Above is kind of how I am trying to get the data to look, so I'll know of the price difference of each product of each source. Then I can control the data, such as ordering by adjustment and seeing which source + product had the largest decrease or increase in a time frame.
I've tried doing a ton of sub-queries, I've probably put in a hundred examples that I've modified from Google. I can piece together parts of this, such as only getting products that have recieved a change of any kind from the past 24 hours. I've tried to merge the last two rows of each product Id, then do a math, and list all the products. It's been 2 days of trying to build this query, is it just best for me to not use queries for everything and do it on my backend?
I've even went to a support site like hackhands and they couldn't figure it out. I've exhausted all of my ideas.
This query breaks down the problem:
1) Getting the records corresponding to start_at time of the window for each product in order to get the baseline price.
2) Gets the the records for the max price for each product in the time frame.
3) Gets the records for the min price for each product in the time frame.
4) Combines 1 and 2 and 3 to form a single record per product and shows the info and the difference between base line price and the highest and lowest in the time frame.
If you only need the bigger of the two you can add and extra layer of select wrapping this query and user GREATER(a,b) to keep one diff or the other.
select BOWPRICE.product_id, BOWPRICE.created_at, BOWPRICE.price,
MAXPRICE.max_price_upd_time, MAXPRICE.max_price, ABS((BOWPRICE.price - MAXPRICE.max_price)) max_price_diff,
MINPRICE.min_price_upd_time, MINPRICE.min_price, ABS((BOWPRICE.price - MINPRICE.min_price)) min_price_diff
from
(
select mainA.product_id, mainA.created_at, mainA.price from SOTEST mainA
where id in (
select id
from SOTEST N
where created_at = (
select min(N1.created_at)
from SOTEST N1
where N1.created_at >= '2017-07-26 00:00:00'
and N1.product_id = N.product_id
)
group by mainT.product_id
)
) BOWPRICE,
(
select mainB.product_id, mainB.updated_at max_price_upd_time, mainB.price max_price from SOTEST mainB
where id in(
select id from SOTEST M
where M.price = (
select max(M1.price)
from SOTEST M1
where M1.created_at >= '2017-07-26 00:00:00'
and M1.created_at < '2017-07-27 00:00:00'
and M1.product_id = M.product_id
group by product_id LIMIT 1
)
)
) MAXPRICE,
(
select mainC.product_id, mainC.updated_at min_price_upd_time, mainC.price min_price from SOTEST mainC
where id in(
select id from SOTEST Q
where Q.price = (
select min(Q1.price)
from SOTEST Q1
where Q1.created_at >= '2017-07-26 00:00:00'
and Q1.created_at < '2017-07-27 00:00:00'
and Q1.product_id = Q.product_id
group by product_id LIMIT 1
)
)
) MINPRICE
where BOWPRICE.product_id = MAXPRICE.product_id
and BOWPRICE.product_id = MINPRICE.product_id

How to group data per week in MySQL?

id modid userid timemodified FROM_UNIXTIME(timemodified,'%d-%m-%Y')
410 32 46 1438971403 03-08-2015
411 32 46 1438971403 03-08-2015
412 66 977 1438971403 07-08-2015
412 66 977 1438971403 07-08-2015
413 67 34 1438971423 07-08-2015
414 68 16 1438971424 07-08-2015
415 132 23 1438972154 07-08-2015
416 134 2 1438972465 08-08-2015
417 115 2 1438996430 08-08-2015
418 130 977 1438996869 08-08-2015
I got this query from framing the last 4weeks ago by calculating from today's date. Now, I want to show the users for 4 weeks individually like week1, week2, week3 & week4, either it could be column wise or row wise, which would be the best.
In detailed, from the above query, I need to separate data from week1 to week4,like
Week4 : No user
Week3 : 2 users (2,977)
Week2 : 4 users (16, 23, 34, 977)
Week1 : 1 user (46)
SET #unix_four_weeks_ago = UNIX_TIMESTAMP(curdate()) - 2419200;
SELECT *,FROM_UNIXTIME(timemodified,'%d-%m-%Y') FROM mod_users WHERE timemodified >= #unix_four_weeks_ago
My guess is that you want to split the user count per week according to the timemodified column. I would use the WEEK() function to do that.
The following SQL would add a weeknumber column to identify the week number:
SELECT WEEK(timemodified) weeknumber, dates.*
FROM dates
Then, if you want to get the distinct user count, you can simply use the following SQL:
SELECT WEEK(timemodified) weeknumber, COUNT(DISTINCT(userid)) users_count
FROM dates
GROUP BY weeknumber
You can also add a WHERE clause to get only certain weeks as you wish. So, to get the last 4 weeks from the 23-08-2015, I would do:
SELECT WEEK(timemodified) weeknumber, COUNT(DISTINCT(userid)) users_count
FROM dates
WHERE WEEK(timemodified) <= WEEK('2015-08-23')
AND WEEK(timemodified) > (WEEK('2015-08-23') - 4)
GROUP BY weeknumber
Let's hope I assumed correctly. :-)

Select more columns with GROUP_CONCAT( ) according to lowest date

I have one table accounts. I have written following query
chk_account= mysql_query("SELECT GROUP_CONCAT( DISTINCT user_id ) AS userlist
FROM `accounts`
");
From this I get users id only. With this query I also want to fetch data price and date with column name price and created but I need only to select with lowest date
I have table structure like this:
id user_id price created
1 31 10 2013-04-09 17:30:15
2 32 20 2013-04-10 20:24:40
3 31 30 2013-04-11 04:44:25
4 33 40 2013-04-12 05:47:18
5 34 50 2013-04-13 19:54:15
6 34 50 2013-04-14 14:27:15
7 35 10 2013-04-15 13:54:45
8 35 60 2013-04-16 12:24:35
9 35 10 2013-04-17 20:34:10
I suspect that you want the earliest date and price for each user. You can do this using group_concat(), using a query such as:
select USER_ID,
substring_index(group_concat(price order by created), ',', 1) as price,
min(created)
from accounts a
group by user_id