MYSQL - select row based on condition, otherwise select other row - mysql

I have a table products which have an association with product_prices.
Each product can have multiple prices for different countries.
Desired outcome: When I run the query, I want to receive all products with 1 single price based on country condition.
Logic
If product has price for the specified country-> then country price
will be displayed.
If product does not have price for the specified
country-> then price for with country_id 400 needs to be displayed.
If product has no price for specified country and for country with id 400 -> then the price with country_id 500 needs to be displayed.
If product has not price for specified country and id 400, and id 500 -> then price which has country with biggest amount of total users will be
displayed.
I did something like this for 1 single product. But I don't know how to achieve these for all products.
select p.id, pp.price, pp.country_id,
(CASE WHEN (pp.country_id=:countryId) then 0 else
(CASE WHEN (pp.country_id=400) then 1 else
(case when (pp.country_id=500) then 2 else 3 END) END) END) as priorityIndex
from products p
inner join product_prices pp on p.id = pp.product_id
inner join countries c on pp.country_id = c.id
where p.id = '00057c218b154d5b838b928a0189ff9f'
order by priorityIndex limit 1;
My data:
Country table
country_id
country_code
total_users
2
FR
10
10
US
100
27
UK
200
400
EU
160
500
GLOBAL
150
Product price table
product_id
price_id
price
country_id
product1
1a
19.99
27
product1
1b
20.99
400
product1
1c
30.99
500
product2
2a
199.99
10
product2
2b
299.99
400
product3
3a
50.99
500
product4
4a
40
2
product4
4a
45
10
My expected output when inserting country 27:
product_id
price
country_id
product1
19.99
27
product2
20.99
400
product3
50.99
500
product4
45
10
I did something like this. But I am not sure only about the last part. In case product price needs to be choosed based on total dealers.
select A.id, A.price_id, A.price, A.country from
(select p.id, pp.id as price_id, pp.price, pp.country_id as country,
ROW_NUMBER() over (PARTITION BY p.id order by (CASE WHEN (pp.country_id=:countryId) then 0 else
(CASE WHEN (pp.country_id=400) then 1 else
(case when (pp.country_id=500) then 2 else 3 END) END) END), c.total_dealer_users desc) AS rowNumber
from products p
inner join product_prices pp on p.id = pp.product_id
inner join countries c on pp.country_id = c.id
where p.id in ('00057c218b154d5b838b928a0189ff9f','054a8caf911e4ff3a594990af20a9611')) as A
where rowNumber=1;

SELECT
*
FROM
(
SELECT
pp.product_id,
pp.price_id,
pp.price,
pp.country_id as country,
ROW_NUMBER() OVER (
PARTITION BY pp.product_id
ORDER BY FIELD(
c.country_id,
27,
400,
500,
c.country_id
),
c.total_users DESC
)
AS rowNumber
FROM
product_price pp
INNER JOIN
country c
ON pp.country_id = c.country_id
)
AS ranked_price
WHERE
rowNumber=1;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=94f8851be7f309d8c4fd1946230d7999

With MySQL 8.0+ ROW_NUMBER() allows to drop all rows except first per product:
SELECT * FROM (
SELECT p.id,
pp.price,
pp.country_id,
ROW_NUMBER() OVER (PARTITION BY p.id ORDER BY FIELD(pp.country_id, 500, 400, 27) DESC) AS `rn`
FROM products AS p
JOIN product_prices AS pp
ON p.id = pp.product_id
WHERE p.id IN (...)
) AS t WHERE t.rn=1

Related

How to get only the maximum values of a joined table?

I have items that can have many prices. I want a result table that has all the items itself and only the latest price of the item for a given region.
item
id
name
1
banana
2
apple
​
price
id
item_id
region
price
date
1
1
USA
10
1-1-2021
2
1
USA
20
2-1-2021
3
2
USA
30
1-1-2021
4
2
Canada
40
2-1-2021
​
result (should look like this)
id
name
region
price
date
1
banana
USA
20
2-1-2021
2
apple
USA
30
1-1-2021
I tried this but it returns all the rows from both tables.
select *
from item
LEFT JOIN price p on p.item_id = item.id
where p.created_at in (
select max(price.created_at)
from price
where p.id = price.id
)
and p.region = 'USA'
​
I fail to understand what I have to do to reduce the result table to only the rcords with the latest prices for each item.
Try this:
SELECT p.id, i.name, p.region, p.price, p.date
FROM price p
INNER JOIN (SELECT item_id, MAX(date) AS date FROM price GROUP BY item_id) t
ON t.item_id = p.item_id AND t.date = p.date
JOIN item i
ON p.item_id = i.id
WHERE p.region = 'USA'
Part of the reason your code did not work is that, in your WHERE clause, you are filtering by the maximum date of all the records in the price table.
This is a typical use-case for row_number():
select *
from item i left join
(select p.*,
row_number() over (partition by id order by created_at desc) as seqnum
from price p
where p.region = 'USA'
) p
on p.item_id = i.id and seqnum = 1;

Simplify in sql [duplicate]

This question already has answers here:
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 1 year ago.
I have some task that I should do it with sql.
Below is the function I currently use.
(SELECT ProductName, sum(Quantity*Price) Revenue, Country
FROM products p
JOIN orderdetails d
ON p.ProductID = d.ProductID
JOIN orders o
ON o.OrderID = d.OrderID
JOIN customers c
ON c.CustomerID = o.CustomerID
WHERE Country =
(
SELECT DISTINCT Country
FROM customers
LIMIT 1
)
GROUP BY ProductName
ORDER BY Revenue DESC
LIMIT 1)
UNION
(SELECT ProductName, sum(Quantity*Price) Revenue, Country
FROM products p
JOIN orderdetails d
ON p.ProductID = d.ProductID
JOIN orders o
ON o.OrderID = d.OrderID
JOIN customers c
ON c.CustomerID = o.CustomerID
WHERE Country =
(
SELECT DISTINCT Country
FROM customers
LIMIT 1,1
)
GROUP BY ProductName
ORDER BY Revenue DESC
LIMIT 1)
UNION
(SELECT ProductName, sum(Quantity*Price) Revenue, Country
FROM products p
JOIN orderdetails d
ON p.ProductID = d.ProductID
JOIN orders o
ON o.OrderID = d.OrderID
JOIN customers c
ON c.CustomerID = o.CustomerID
WHERE Country =
(
SELECT DISTINCT Country
FROM customers
LIMIT 2,1
)
GROUP BY ProductName
ORDER BY Revenue DESC
LIMIT 1)
My task is "Find best selling products based on revenue for each country!"
The result I want is below:
ProductName
Revenue
Country
Tofu
279
Argentina
Côte de Blaye
18445
Austria
You can access the data I use from this link RawDatabase
the sample data that I use is like this
ProdName
Country
Revenue
coco
Argentina
120
bread
Austria
10000
crunch
Austria
13265
Cote de Blaye
Austria
18445
milk
Argentina
254
Tofu
Argentina
279
From this data I want to select only the best product for each country by revenue. In the data there were 21 country. What should I do so I can get the result below
ProductName
Revenue
Country
Tofu
279
Argentina
Côte de Blaye
18445
Austria
The only way in my mind is only filtering the data by each country then get the best product then union all of them like the code I give on top. I wonder if there's another way.
Using row_number window function or compare to country maxrevenue
DROP TABLe if exists t;
create table t
(ProdName varchar(20), Country varchar(20), Revenue int);
insert into t values
('coco' ,'Argentina' ,120),
('bread' ,'Austria' ,10000),
('crunch','Austria' ,13265),
('Cote de Blaye' ,'Austria', 18445),
('milk' ,'Argentina' ,254),
('Tofu' ,'Argentina' ,279);
select *
from
(
select prodname,country,revenue,
row_number() over(partition by country order by revenue desc) rn
from t
) s
where rn = 1;
or
select *
from t
join (select t.country,max(t.revenue) maxrevenue from t group by t.country) t1
on t1.country = t.country and t1.maxrevenue = t.revenue;
+---------------+-----------+---------+----+
| prodname | country | revenue | rn |
+---------------+-----------+---------+----+
| Tofu | Argentina | 279 | 1 |
| Cote de Blaye | Austria | 18445 | 1 |
+---------------+-----------+---------+----+
2 rows in set (0.001 sec)

how to get max rows from 2 transactional tables. MySQL

I have two mysql transactional tables and and two lookup tables. I want to select max(id) from each of the transactional tables, combine the results with lookup tables and combine into one row. I seem unable to find solutions so far. Here is my tables. Stocks and Prices are transactional while Vehicle and Models are lookup tables.
Vehicles table
id name
1 Toyota
2 Suzuki
Models table
id vehicle_id name
1 1 Corolla
2 2 Swift
3 1 Prado
4 2 Vitara
Stocks table
id vehicle_id model_id qty
1 1 1 50
2 2 2 77
3 1 1 40
4 2 2 30
Prices table
id vehicle_id model_id price
1 1 1 500
2 2 2 777
3 1 1 600
4 2 2 1000
Expected results
id vehicle_id model_id qty price vname mname
1 1 1 40 600 Toyota Corolla
2 2 2 30 1000 Suzuki Swift
Here is what I've tried among countless trials
select s.*, b.name vehicle, m.name model, p.price
from stocks s, vehicles b, models m, prices p
where s.id in (select max(id) id from stocks
where s.vehicle_id = b.id and s.model_id = m.id and s.vehicle_id = p.vehicle_id and s.model_id = p.model_id
group by vehicle_id, model_id)
order by id;
Running the above query doesn't give me what I want and it crushes the PC. I have to restart. How can I achieve the expected outcome?
If you are using MySQL 8 you can use window functions and common table expressions for latest(based on maximum id per vehicle and model group) prices and qty for vehicle and models
with pricescte as (select *,
rank() over (partition by vehicle_id,model_id order by id desc) AS price_rank
from prices),
stockcte as (select *,
rank() over (partition by vehicle_id,model_id order by id desc) AS stock_rank
from stocks)
select v.id,
v.name,
m.id as model_id,
m.name,
s.qty,
p.price
from vehicles v
join models m on v.id = m.vehicle_id
join stockcte s on v.id = s.vehicle_id
and m.id = s.model_id
join pricescte p on v.id = p.vehicle_id
and m.id = p.model_id
where s.stock_rank = 1
and p.price_rank = 1
DEMO
If you are not on latest version of MySQL < 8 you could use a query like
select v.id,
v.name,
m.id as model_id,
m.name,
s.qty,
p.price
from vehicles v
join models m on v.id = m.vehicle_id
join (
select *
from stocks st
where id = (
select max(id)
from stocks
where st.vehicle_id =vehicle_id
and st.model_id = model_id
)
) s
on v.id = s.vehicle_id
and m.id = s.model_id
join (
select *
from prices pr
where id = (
select max(id)
from prices
where pr.vehicle_id =vehicle_id
and pr.model_id = model_id
)
) p on v.id = p.vehicle_id
and m.id = p.model_id
DEMO

Getting data form 2 tables and summing values of the result

I have 2 tables with information: ID, persona_id, total_amount
The persona ID can repeat dozen of times. So i get all the one persons id total_amount with query:
select d.id as debt_id, p.name as persona, sum(d.total_amount) as total_amount
from debt d
join persons p on d.persona_id = p.id group by p.name
I want to get data from each table in one query and do aritmethic propertys with the total_amount column and return it as 1 tabel.
TABLE 1
id persons_id total_amount
1 2 50
2 3 100
3 2 200
4 5 300
5 1 500
TABLE 2
id persons_id total_amount
1 2 25
2 1 100
3 5 50
4 3 100
5 4 300
As a result i want to get the 2 tables comined with arithmetic operation (-, +, / , * ) of Total amount columns.Basicaly a change to get the ending result total amount in form i want for different cases.
What worked for me based on JohnHC answear was :
select c.id, c.persona_id, c.total_amount - d.total_amount as new_total
from ( select c.id , c.persona_id, sum(c.total_amount) as total_amount from credit c
join persons p on c.persona_id = p.id
group by p.name) c
inner join ( select d.id, d.persona_id, sum(d.total_amount) as total_amount from debt d
join persons p on d.persona_id = p.id
group by p.name) d
on c.persona_id = d.persona_id
group by c.id, c.persona_id
If you want the total, try:
select id, person_id, sum(total_amount)
from
(
select id, person_id, total_amount
from table1
union all
select id, person_id, total_amount
from table2
)
group by id, person_id
If you want to do other things, try:
select t1.id, t1.person_id, t1.total_amount [+ - / *] t2.total_amount as new_total
from table1 t1
inner join table2 t2
on t1.id = t2.person_id
group by t1.id, t1.person_id

MYSQL query related data from another table

I would like to query related data from an additional table, where I am able to get the lowest associated value...
Example of two tables
products
id name description
0 product_1 short description of 1
1 product_2 short description of 2
prices
id product_id option price personal
0 1 3 10.00 1
1 0 2 15.00 1
2 1 3 5.00 0
3 1 3 8.00 0
4 0 2 7.00 1
Output needed
id name description price option
0 product_1 short ... 7.00 2
1 product_2 short ... 10.00 3
The query I am basically attempting to make is one which gets all associated fields, gets the associated data from prices where personal = 1 and has the lowest price.
Current query (getting lowest price but not associated option)
SELECT products.*, prices.option,
(SELECT ROUND( MIN( price ), 2) FROM prices WHERE product_id = products.id AND personal = 1) AS price
FROM products
ORDER BY price_low ASC
What about using a join instead of a subquery and grouping by products? It should look something like this:
SELECT products.*, ROUND(MIN(prices.price), 2), prices.option
FROM products
INNER JOIN prices ON products.id = prices.product_id
WHERE prices.personal = 1
GROUP BY products.id
I tried it out on SqlFiddle, here are the results.
you could use an inner join and a subselect with and group by for get the min price
SELECT p.id, p.name, p.description, pr.option, pr.price
FROM products as p
inner join (
select product_id, min(price) as price
from price
where personal = 1
group by product_id
) t on t.product_id= p.id
inner join price as pr on pr.product_id = p.id and pr.price = t.price
where pr.personal =1
group by p.id, p.name, p.description, pr.option
ORDER BY pr.price ASC
Thank you for the feedback on this question. After a lot of trail and error I finally got the needed result, here it is:
SELECT products.*, b.*, products.id as id FROM `products`
LEFT JOIN
(SELECT * FROM
( SELECT * FROM prices ORDER BY product_id ASC, price ASC )
as c GROUP BY product_id)
AS b ON b.product_id = products.id
WHERE `price` IS NOT NULL ORDER BY `product_id` ASC