getting max data in multiple join - mysql

i have the data like this
purchase_order (po)
po_id pi_id store
112 789 ABC
113 101 DEF
114 102 GHI
115 103 JKL
purchase_items (pi)
pi_id barcode price date
789 123 500 2020-06-04
101 123 400 2020-06-03
102 456 500 2020-06-02
103 456 400 2020-06-01
product
barcode product
123 milk
456 tea
I want to get the latest price for each barcode if the store exclude "GHI"
expected result
barcode price
123 500
456 400
how should i do ?

One option uses a correlated subquery:
select
p.barcode,
(
select pi.price
from purchase_items pi
inner join purchase_orders po on po.pi_id = pi.pi_id
where pi.barcode = p.barcode and po.store <> 'GHI'
order by pi.date desc limit 1
) price
from product p

row_number() is one method:
select pi.barcode, pi.price
from (select pi.*, row_number() over (partition by pi.barcode order by pi.date desc) as seqnum
from purchase_items pi
where not exists (select 1
from purchase_orders po
where po.pi_id = pi.pi_id and po.store = 'GHI'
)
) pi
where seqnum = 1;

Use join with max-function:
select pi.barcode, max(pi.price)
from purchase_items pi
join purchase_order po on po.pi_id=pi.pi_id
where po.store!='GHI'
group by pi.barcode

A way of using subqueries.
SELECT
DISTINCT
a.`barcode`,
a.`price`
FROM
purchase_items a
WHERE a.`date` =
(SELECT
MAX(`date`)
FROM
purchase_items i
WHERE i.barcode = a.barcode
AND 'GHI' !=
(SELECT
store
FROM
purchase_order
WHERE pi_id = i.`pi_id`
LIMIT 1)) ;

Related

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

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

How can I retrieve Similar Orders In Mysql?

i need a query that should first look the oldest order which has status 0 (zero). and retrieves all the similar orders of that kind(matches exact total qty, itemSku and number of distinct items ordered).
***OrdersTable***
ID OrderNumber CustomerId Status created_at
1 123456 1 0 2018-01-01
2 234567 1 0 2018-01-02
3 345678 1 0 2018-01-03
4 456789 1 0 2018-01-04
***PurchasedProductsTable***
OrderId itemSku Qty
1 1000001 1
1 1000002 2
2 1000001 3
3 1000001 1
3 1000002 2
4 1000001 3
In the above table the query should first look at the oldest (created_at ASC) order (i.e with Id 1) having status 0 (in order table). and along with that order it should retrieves all the other orders that matches the same itemSku, qty and total distinct items count (in purchasedProducts table).
here order 1 and 3 matches the same itemSKu (1000001 and 1000002) and qty ( 1 and 2) and both have (2) distinct items count respectively so order 1 and 3 should be retrived at first.and when i marked order 1 and 3 as shipped (i.e chang status to 2).
and if i run query again it should retrive similar oders. now order 2 and 4 as order 2 and 4 are similar orders. (have same itemSkus (1000001, Qty (3) and distinct items count (1)).
please help thanks
You have to go trough your tables two times :)
Something like this :
SELECT DISTINCT O2.ID
FROM OrdersTable O1
INNER JOIN PurchasedProductsTable P1 ON O1.ID = P1.OrderId
INNER JOIN PurchasedProductsTable P2 ON P1.itemSku = P2.itemSku
AND P1.Qty = P2.Qty
INNER JOIN OrdersTable O2 ON O2.ID = P2.OrderId
WHERE O1.ID =
(SELECT ID FROM OrdersTable WHERE Status = 0
ORDER BY created_at ASC LIMIT 1)
AND (SELECT COUNT(*) FROM PurchasedProductsTable WHERE OrderId = O1.ID)
= (SELECT COUNT(*) FROM PurchasedProductsTable WHERE OrderId = O2.ID)
ORDER BY O2.ID ASC;
https://www.db-fiddle.com/f/65t9GgSfqMpzNVgnrJp2TR/2
You can get the earliest order via a limit and ordered by the date.
Then you can left join to get that order and any other order that at least has the same items.
Then once you have those order id's from the sub-query result, you can get the order details.
SELECT o.*
FROM
(
SELECT DISTINCT ord2.ID as OrderId
FROM
(
SELECT ID, CustomerId, Status
FROM OrdersTable
WHERE Status = 0
ORDER BY created_at
LIMIT 1
) AS ord1
JOIN PurchasedProductsTable AS pprod1
ON pprod1.OrderId = ord1.ID
LEFT JOIN OrdersTable ord2
ON ord2.CustomerId = ord1.CustomerId
AND ord2.Status = ord1.Status
LEFT JOIN PurchasedProductsTable pprod2
ON pprod2.OrderId = ord2.ID
AND pprod2.itemSku = pprod1.itemSku
AND pprod2.Qty = pprod1.Qty
GROUP BY ord1.CustomerId, ord1.ID, ord2.ID
HAVING COUNT(pprod1.itemSku) = COUNT(pprod2.itemSku)
) q
JOIN OrdersTable AS o ON o.ID = q.OrderId;
Test on RexTester here

SQL query in Mysql

I am using php and Mysql and need help to display UNIQUE results for a price comparison.
My tables are:
tblItems
ID | SKU | Shop | Name | Size
1 S1 Shop1 A S
2 S2 Shop1 B M
3 S3 Shop1 C L
4 S4 Shop2 A S
tblProductFeed
ID | SKU | Shop | Price
1 S1 Shop1 12
2 S2 Shop1 14
3 S3 Shop1 15
4 S4 Shop2 11
The idea is a price comparison.
I want to compare all records where Size='S'. But I only want to get the lowest price from tblProductFeed, and I only want one record where the price is lowest.
My thoughts are like:
Get SKU, Shop and Name from tblItems where Size='S'
Compare matching rows in tblProductFeed where SKU and Shop are matching
Get the lowest price from tblProductFeed
Display only one DISTINCT Name from TblItems and with the lowest price
Results:
Name | Price
A 11
B 14
C 15
Maybe my idea is bad, please correct me.
Before I decided to scrap data and build to tables my sql was like this, might help to get some inspiration.
SELECT tbl.*
FROM tblProductFeed tbl
INNER JOIN (SELECT Size
, MIN(Price) MinPrice
FROM tblProductFeed
WHERE Size = '". $str ."'
GROUP BY Size) tbl1
ON tbl1.Size = tbl.Size
WHERE tbl1.MinPrice = tbl.Price;
try this : to retrieve lowest price for data with size 'S'
select a.Name, a.Shop, b.Price as minprice from tblItems as a
inner join tblProductFeed as b on
a.id = b.id and
a.SKU = b.SKU and
a.Shop = b.shop
where a.size = 'S' and b.price in (select min(price) from tblProductFeed)
Result :
Or if you want to get all item with lowest price try this :
select name,min(price)minprice from
(select a.id, a.SKU, b.shop, a.name, a.size, b.Price from tblItems as a
inner join tblProductFeed as b on
a.id = b.id and
a.SKU = b.SKU and
a.Shop = b.shop
where b.price in (select min(price) from tblProductFeed group by price)) a
group by name
Result :

MySQL includes a specific row with order by

Given 2 tables, I want to generate top 3 highest amount from [Purchase] table.
Additional criteria is [Crocs] must be included in top 3 of the records.
I have following SQL, but it cannot generates the result as I wanted (Result A), please guide me on how to pull out the result in Result B. Thank you.
Table (Purchase):
Purchase_ID | StoreID | Amount
------------|---------|--------
1 | 21 | 22
2 | 23 | 13
3 | 25 | 6
4 | 26 | 23
5 | 28 | 18
Table (Store):
Store_ID | StoreName
---------|----------
21 | Adidas
22 | Nike
23 | Puma
24 | New Balance
25 | Crocs
26 | Converse
SQL:
SELECT IF(SUM(amount) IS NULL, 0, SUM(amount)) as totalAmount
FROM (
SELECT a.amount
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
GROUP BY a.amount
HAVING b.StoreName = 'Crocs'
ORDER BY a.amount DESC
LIMIT 3
) t
Result A: $6
Explanation A: Amount of Crocs is $6
Result B: $51
Explanation B: Total Amount of top 3 = $22 (Adidas) + 23 (Puma) + $6 (Crocs)
The answer from scaisEdge is almost right, but the first query could also return a row with crocs and the sorting is wrong (order by max(a.amount) limit 2 means that the lowest 2 results will be shown). Additionally you could wrap the query in another select query to sort the results
SELECT * FROM (
SELECT b.storename, max(a.amount) as maxAmount
FROM purchase a
INNER JOIN store b ON a.store_id = b.storeid
WHERE b.storename != 'crocks'
GROUP BY a.storename
ORDER BY max(a.amount) DESC
LIMIT 2
UNION
SELECT b.storename, a.amount as maxAmount
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
WHERE b.storename='crocks'
ORDER BY a.amount DESC
LIMIT 1
) ORDER BY maxAmount DESC
You could use an union
SELECT b.storename, max(a.amount)
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
GROUP BY a.storename
order by max(a.amount) limit 2
union
SELECT b.storename, a.amount
FROM purchase a
INNER JOIN store b
ON a.store_id = b.storeid
where b.storename='crocks'
try this one:
SELECT sum(amount)as sum_amount,a.store_id,storename,category from
(select amount,store_id from tbl_purchase) as a
inner JOIN
(select store_id,storename,category from tbl_store)as b on a.store_id = b.store_id where b.category = 'supermarket' GROUP BY category

SUMs from multiple JOINed tables MSSQL

I'm trying to generate a report that shows outstanding balances based on dynamic calculations across several tables. Currently this report is being generated using a server side language that runs a query, loops through the query, and runs another query against the result. My goal is to perform all the calculations within MSSQL.
Here are the tables / columns with some example data:
TEST_ORDER Table
orderid customerid serviceamount
1001 2001 75.00
1002 2002 85.00
1003 2001 25.00
1004 2003 10.00
TEST_CUSTOMER Table
customerid customername
2001 'Initech'
2002 'Dunder Mifflin'
2003 'Paper Street Soap Co'
TEST_ORDERITEM Table
orderitemid orderid itemamount
5001 1001 50.00
5002 1001 150.00
5003 1002 15.00
5004 1004 200.00
5005 1004 200.00
TEST_TRANSACTION Table
transactionid orderid amount
9001 1001 75.00
9002 1002 25.00
9003 1002 50.00
9004 1003 55.00
9005 1001 50.00
9006 1001 150.00
The results I'm looking for are:
orderid customername totalorder totalpaid balance
1001 'Initech' 275.00 275.00 0.00
1002 'Dunder Mifflin' 100.00 75.00 -25.00
1003 'Initech' 25.00 55.00 30.00
1004 'Paper Street Soap Co' 410.00 0.00 -410.00
Here's the query I came up with:
SELECT
o.orderid, c.customername, o.serviceamount,
(o.serviceamount + SUM( i.itemamount )) AS totalorder,
SUM( t.amount ) AS totalPaid,
( SUM( t.amount ) - (o.serviceamount + SUM( i.itemamount )) ) AS balance
FROM
test_order o
INNER JOIN test_customer c ON o.customerID = c.customerID
LEFT OUTER JOIN test_transaction t ON o.orderID = t.orderID
LEFT OUTER JOIN test_orderitem i ON o.orderID = i.orderID
WHERE
1=1
GROUP BY
o.orderid, c.customername, o.serviceamount,
o.serviceamount
And here are the results:
orderid customername totalorder totalpaid balance
1001 'Initech' 675.00 550.00 -125.00
1002 'Dunder Mifflin' 115.00 75.00 -40.00
1003 'Initech' null 55.00 null
1004 'Paper Street Soap Co' 410.00 null null
The problems I'm having are:
1. Records in test_orderitem are duplicated, e.g. for order 1002, the serviceamount is $85 and there is only one orderitem for $15, however, th totalorder is calculated for $115. I think the problem has to do with the JOIN iterating twice.
2. 'null' is showing up when no records are returned (for obvious reasons). I haven't started working on this, as I've been testing in MySQL, and I want to figure out the JOIN issue before carrying this back into MSSQL.
Thank you for any assistance you might be able to offer...
For MS-SQL: Here you, just replace table names in final query with your own.
DECLARE #Test_Order TABLE (orderid int, customerid int, serviceamount money)
INSERT INTO #Test_Order(orderid,customerid,serviceamount)
SELECT 1001,2001,75.00 UNION ALL
SELECT 1002,2002,85.00 UNION ALL
SELECT 1003,2001,25.00 UNION ALL
SELECT 1004,2003,10.00
DECLARE #Test_Customer TABLE (customerid int, customername varchar(100))
INSERT INTO #Test_Customer(customerid,customername)
SELECT 2001,'Initech' UNION ALL
SELECT 2002,'Dunder Mifflin' UNION ALL
SELECT 2003,'Paper Street Soap Co'
DECLARE #Test_OrderItem TABLE (orderitemid int, orderid int, itemamount money)
INSERT INTO #Test_OrderItem (orderitemid, orderid, itemamount)
SELECT 5001,1001,50.00 UNION ALL
SELECT 5002,1001,150.00 UNION ALL
SELECT 5003,1002,15.00 UNION ALL
SELECT 5004,1004,200.00 UNION ALL
SELECT 5005,1004,200.00
DECLARE #Test_Transaction TABLE (transactionid int, orderid int, amount money)
INSERT INTO #Test_Transaction (transactionid, orderid, amount)
SELECT 9001,1001,75.00 UNION ALL
SELECT 9002,1002,25.00 UNION ALL
SELECT 9003,1002,50.00 UNION ALL
SELECT 9004,1003,55.00 UNION ALL
SELECT 9005,1001,50.00 UNION ALL
SELECT 9006,1001,150.00
SELECT O.orderid,
C.customername,
SUM(ISNULL(O.serviceamount,0)+ISNULL(I.itemamount,0)) AS totalorder,
SUM(ISNULL(T.amount,0)) AS totalpaid,
(SUM(ISNULL(T.amount,0))) - (SUM(ISNULL(O.serviceamount,0)+ISNULL(I.itemamount,0))) as balance
FROM #Test_Customer C
JOIN #Test_Order O
ON C.customerid=O.customerid
LEFT JOIN (SELECT orderid, SUM(itemamount) AS itemamount FROM #Test_OrderItem GROUP BY orderid) I
ON O.orderid=I.orderid
LEFT JOIN (SELECT orderid, SUM(amount) AS amount FROM #Test_Transaction GROUP BY orderid) T
ON O.orderid=T.orderid
GROUP BY O.orderid, C.customername
ORDER BY O.orderid, C.customername
http://sqlfiddle.com/#!9/ae0be/1
SELECT
o.orderid,
c.customername,
(coalesce(o.serviceamount,0)+coalesce(i.itemsum,0)) totalorder,
t.t_sum totalpaid,
(coalesce(t.t_sum,0)-coalesce(o.serviceamount,0)-coalesce(i.itemsum,0)) balance
FROM TEST_ORDER o
LEFT JOIN (
SELECT orderid, SUM(itemamount) itemsum
FROM TEST_ORDERITEM
GROUP BY orderid
) i
ON o.orderid = i.orderid
LEFT JOIN (
SELECT orderid, SUM(amount) t_sum
FROM TEST_TRANSACTION
GROUP BY orderid
) t
ON o.orderid = t.orderid
LEFT JOIN TEST_CUSTOMER c
ON o.customerid = c.customerid
ORDER BY o.orderid
Give this a try:
SELECT
summary.orderid,
summary.customername,
summary.totalorder,
summary.totalpaid,
summary.totalorder - summary.totalpaid AS balance
FROM
(SELECT
o.orderid,
c.customername,
(SELECT SUM(oi.itemamount) FROM test_orderitem oi WHERE oi.orderid = o.orderid) AS totalorder,
(SELECT IFNULL(SUM(t.amount), 0) FROM test_transaction t WHERE t.orderid = o.orderid) AS totalpaid
FROM
test_order o,
test_customer c
WHERE
o.customerid = c.customerid) summary