This question already has answers here:
MySQL select before after row
(4 answers)
Closed 3 years ago.
I have a table with a value column. I need to select the line chosen by the user (on the front), one line above and one line below, so:
My 'orders' table rows
----------------------------------------------
id | price |
----------------------------------------------
1 | 1500.00 |
2 | 1380.00 |
3 | 1880.00 |
4 | 1900.00 |
5 | 1450.00 |
6 | 1700.00 |
If the person chose: 1450.00, I want to bring 1450.00, 1380.00 and 1500.00. Is it possible to make this MySql select? or will i have to do this in php?
I only have the initial query for now:
SELECT * FROM `orders`
ORDER BY price;
You could select max and min price to get row. Let try this code:
SELECT *
FROM orders A
WHERE A.price = 1450.00
OR A.price = (SELECT MAX(B.price) FROM orders B WHERE B.price < 1450.00)
OR A.price = (SELECT MIN(B.price) FROM orders B WHERE B.price > 1450.00)
One way you could do it is using 2 MySQL statements. Something like:
SELECT * FROM `orders` WHERE `price`>=$user_price ORDER BY `price` ASC LIMIT 2
Which should give you the user's selected price and the row above. Then you can execute
SELECT * FROM `orders` WHERE `price`<$user_price ORDER BY `price` DESC LIMIT 1
Which should give you the row below.
I'm not sure if there's a way to combine the two queries, but if I do find anything I'll update the answer :)
Here's how to get this, use max() on < 1450 and and min() on > 1450
select * from test
where price in
(select price from test where price = 1450)
or price in
(select max(price) from test where price < 1450)
or price in
(select min(price) from test where price > 1450)
instead of using or, use in instead to more cleaner.
select * from test
where price in (1450,
(select max(price) from test where price < 1450),
(select min(price) from test where price > 1450))
see dbfiddle
If the prices are unique and you want exactly three rows:
(select o.*
from orders o
where o.price <= :price
order by o.price
limit 2
) union all
(select o.*
from orders o
where o.price > :price
order by o.price desc
limit 1
);
Your question does not clarify what to do in these cases:
When the price is less than or equal to the minimum price.
When the price is greater than or equal to the maximum price.
When there are duplicate prices.
I think this query should work
select price
from orders
where price <= (select min(price) from orders where price > 1450.00)
order by price desc
limit 3
Related
I've got the following table:
booking_id
user_id
11
1
12
76
13
932
14
1
15
626
16
1
17
3232
I want to access the 2nd maximum booking_id for user 1.
The expected result is user_id = 1, booking_id = 14.
I've been working over these hellish flames for way too long, this doesn't do any good:
select booking.user_id, b1.booking_id from booking
left join(select
user_id,
booking_id
from booking
where booking_id = (select
max(booking_id)
from booking
where booking_id <> (select
max(booking_id)
from booking))
group by user_id)
as b1 on b1.user_id = booking.user_id
where booking.user_id = '1'
Please note I've managed to do it as a calculated column but that's useless, I need the derived table.
If you are using MySQL, you can avoid the (rather messy) double sub-query by using LIMIT & OFFSET
Just add order by booking_id desc LIMIT 1 OFFSET 1 and you will get the second highest booking_id. For example ...
select * from booking where user_id = 1 order by booking_id desc OFFSET 1 LIMIT 1
I tested this on one of my tables & it worked fine. If you have an index on booking_id it should be really fast.
If you want the second highest booking for the user who holds the highest booking, then this should work
SELECT * FROM booking
WHERE user_id in
(select user_id from booking order by booking_id desc limit 1)
ORDER BY booking_id DESC LIMIT 1 OFFSET 1
The sub-query finds the user_id of the user with the highest booking, then the main query finds their second highest booking
A simple way to do it is using LIMIT OFFSET:
SELECT *
FROM booking
WHERE user_id = 1
ORDER BY booking_id DESC
LIMIT 1 OFFSET 1
Demo here
By using the answer in this question What is the simplest SQL Query to find the second largest value? https://stackoverflow.com/a/7362165/14491685
you can integrate with your query to get it like this:
select * from booking
where booking_id =
(select max(booking_id) from booking
where user_id =1
and booking_id not in (SELECT MAX(booking_id ) FROM booking ))
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.
I have a problem with a mySQL query, as it takes too much times (about 12 seconds) the table have about 70,000 records
here is my query:
SELECT DATE(`orders`.`date`) AS `dater`,
COUNT(*) AS `amount`,
( SELECT SUM(`amount`) FROM `payment`
WHERE DATE(`dater`)=DATE(`payment`.`posted_date`)
) AS `charge`
FROM `orders`
GROUP BY `dater`
ORDER BY `dater` DESC
LIMIT 0,10
As you can see there is 2 tables
1. table: orders
it is the orders tables here we have 1 main column: date (we need here to count the orders per day)
sample:
--------------------
date | id
--------------------
01-01-2017 | 1
--------------------
01-01-2017 | 2
--------------------
01-02-2017 | 3
--------------------
table: payment:
it is the payments table here we have 2 main columns: posted_date, amount (we need here to get the sum of amount field for the every day)
sample:
--------------------
posted_date | amount
--------------------
01-01-2017 | 100
--------------------
01-01-2017 | 50
--------------------
01-02-2017 | 200
--------------------
so at end the results should be [date, amount, charge]
sure in less time, as it is impossible to go with this 12 seconds :D
I see that the problem is on this internal SELECT:
(SELECT SUM(`amount`) FROM `payment`
WHERE DATE(`dater`)=DATE(`payment`.`posted_date`)
) AS `charge`
any suggestion of how can I avoid making SELECT inside the SELECT query?
You have a correlated subquery. MySQL's query planner handles those in a naive way, to put it politely.
Refactor your query to use a join instead. Your correlated subquery would look like this as a joinable query.
SELECT SUM(amount) charge,
DATE(posted_date) posted_date
FROM payment
GROUP BY DATE(posted_date)
This gets one row per day from the payment table.
Next, you need to get a similar sort of result from your orders table.
SELECT DATE(date) AS dater,
COUNT(*) AS amount
FROM orders
GROUP BY DATE(date)
Then, join those two together
SELECT a.dater, a.amount, b.charge
FROM (
SELECT DATE(date) AS dater,
COUNT(*) AS amount
FROM orders
GROUP BY DATE(date)
) a
LEFT JOIN (
SELECT SUM(amount) charge,
DATE(posted_date) posted_date
FROM payment
GROUP BY DATE(posted_date)
) b ON a.dater = b.posted_date
ORDER BY a.dater DESC
LIMIT 0,10
It's necessary to join two subqueries here, because you need two aggregates by date for the join to work.
I have a table with prices and dates on product:
id
product
price
date
I create a new record when price change. And I have a table like this:
id product price date
1 1 10 2014-01-01
2 1 20 2014-02-17
3 1 5 2014-03-28
4 2 25 2014-01-05
5 2 12 2014-02-08
6 2 30 2014-03-12
I want to get last price for all products. But when I group with "product", I can't get a price from a row with maximum date.
I can use MAX(), MIN() or COUNT() function in request, but I need a result based on other value.
I want something like this in final:
product price date
1 5 2014-03-28
2 30 2014-03-12
But I don't know how. May be like this:
SELECT product, {price with max date}, {max date}
FROM table
GROUP BY product
Alternatively, you can have subquery to get the latest get for every product and join the result on the table itself to get the other columns.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT product, MAX(date) mxdate
FROM tableName
GROUP BY product
) b ON a.product = b.product
AND a.date = b.mxdate
I think the easiest way is a substring_index()/group_concat() trick:
SELECT product,
substring_index(group_concat(price order by date desc), ',', 1) as PriceOnMaxDate
max(date)
FROM table
GROUP BY product;
Another way, that might be more efficient than a group by is:
select p.*
from table t
where not exists (select 1
from table t2
where t2.product = t.product and
t2.date > t.date
);
This says: "Get me all rows from the table where the same product does not have a larger date." That is a fancy way of saying "get me the row with the maximum date for each product."
Note that there is a subtle difference: the second form will return all rows that on the maximum date, if there are duplicates.
Also, for performance an index on table(product, date) is recommended.
You can use a subquery that groups by product and return the maximum date for every product, and join this subquery back to the products table:
SELECT
p.product,
p.price,
p.date
FROM
products p INNER JOIN (
SELECT
product,
MAX(date) AS max_date
FROM
products
GROUP BY
product) m
ON p.product = m.product AND p.date = m.max_date
SELECT
product,
price,
date
FROM
(SELECT
product,
price,
date
FROM table_name ORDER BY date DESC) AS t1
GROUP BY product;
I have created a bunch of MySQL script that import products into magento, i have used a GROUP BY to group products into configurable products
in the past, i have used MIN() to get the lowest price in the group and used that value as the price for the configurable products but as of late there have been some cases like this
Simple Product ID | Price ($)
------------------------------
1001 | 10
1002 | 10
1003 | 5
1004 | 10
1005 | 20
1006 | 10
in this situation, when i create the configurable product, MIN(Price) would return 5, i only just learned that with configurable products i could have a negative difference which means now i need to change my code so instead of getting minimum (5) i get the Mode (10)
I figured i just have to replace the MIN() in my query with MODE() but there doesn't seem to be a MODE() function for MySQL, here's an example of the query i'm using
INSERT INTO import_table
(
product_id, stock_id, price
)
SELECT ODT.product_id, ODT.stock_id, MIN(ODT.Price)
FROM org_data_table AS ODT
GROUP BY ODT.stock_id
Is there a function that already exists to get the Mode? if not then what do i need to do to get it, i would like to just change the MIN() part of my query
You can retrieve the MODE of price through below query
SELECT ODT.Price as price, count(ODT.Price) AS cnt
FROM org_data_table as ODT
GROUP BY ODT.price
ORDER BY cnt DESC
LIMIT 1;
This will return the Price Mode value, and then you can utilize this in SELECT statement as below so you can insert the Mode value, see below query:
INSERT INTO import_table
(
product_id, stock_id, price
)
SELECT ODT.product_id, ODT.stock_id, (SELECT ODT.Price as price
FROM org_data_table as ODT
GROUP BY ODT.price
ORDER BY count(ODT.Price) DESC
LIMIT 1) as price
FROM org_data_table AS ODT
GROUP BY ODT.stock_id
This can be a little tricky because MODEs can have multiple prices -- if 2 prices have the same count, then they are both considered the MODE.
This query should return any records from the org_data_table whose price is in the MODE:
SELECT DISTINCT T.Product_Id, T.Stock_Id, T.Price
FROM org_data_table T
JOIN (
SELECT COUNT(*) cnt, Price
FROM org_data_table
GROUP BY Price
HAVING COUNT(*) = (
SELECT Max(cnt)
FROM (
SELECT COUNT(*) cnt, Price
FROM org_data_table
GROUP BY Price
) t
)
) T2 ON T.Price = T2.Price
And here is some sample Fiddle: http://sqlfiddle.com/#!2/8f1a2/1
Obviously you can add:
INSERT INTO import_table (product_id, stock_id, price)
before the query to insert the records.
Hope this helps.