How can I retrieve Similar Orders In Mysql? - 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

Related

max frequency in sql

i have 2 tables (mysql)
tbl_products
tbl_counting
I have collected "rack" & "stock" information by 3 users in tbl_counting table.
I want to update "tbl_products.rack" & "tbl_products.stock" with tbl_counting data
with this condition (like Result):
Each "Rack" & "Stock" information must be collected by at least 3 users
tbl_products.rack" & "tbl_products.stock" must update with highest frequency (At least 2 times) of "Rack" or "Stock" information
If "Rack" or "Stock" information was repeated less than 2 times, specify field with "Unknow". (like result)
Thanks
1. tbl_products (befor update)
id
product_name
rack
stock
1
apple
2
orange
3
strawberry
2. tbl_counting
product_id
user_id
rack
stock
1
1
A-1-1
20
1
2
A-1-1
10
1
3
B-1-1
20
2
1
C-1-1
10
2
2
D-2-1
30
2
3
A-3-1
30
3
1
X-3-1
25
3
2
X-1-1
10
3
3
X-3-1
25
Result: tbl_products(after UPDATE)
id
product_name
rack
stock
1
apple
A-1-1
20
2
orange
Unknown
30
3
strawberry
X-3-1
25
select
tbl_counting.product_id,
tbl_counting.rack,
MAX(tbl_counting.stock),
count(*) as freq
from tbl_counting
group by tbl_counting.product_id
having count(*) =(select max(freq)
from (select product_id,count(*) as freq
from tbl_counting group by product_id) tbl_counting)
You Can check this
SELECT
product_id p,
shelf,
floor,
line,
stock
FROM
user_product
GROUP BY
product_id,
stock,
floor,
line,
shelf
HAVING
stock = ( SELECT stock FROM user_product WHERE product_id = p GROUP BY stock ORDER BY count(*) DESC LIMIT 1 ) AND
floor = ( SELECT floor FROM user_product WHERE product_id = p GROUP BY floor ORDER BY count(*) DESC LIMIT 1 ) AND
line = ( SELECT line FROM user_product WHERE product_id = p GROUP BY line ORDER BY count(*) DESC LIMIT 1 ) AND
shelf = ( SELECT shelf FROM user_product WHERE product_id = p GROUP BY shelf ORDER BY count(*) DESC LIMIT 1 );
Here is one possible solution. The first two cte gets you the count of users and distinct racks by product, which are used in the next cte to filter the records that meet the given criteria.
UPDATE tbl_products P
JOIN (
WITH uses_count AS
(
SELECT product_id,
COUNT(DISTINCT user_id) AS num_users
FROM tbl_counting
GROUP BY product_id
)
, rack_count AS
(
SELECT product_id,
rack,
COUNT(*) AS freq
FROM tbl_counting
GROUP BY product_id, rack
)
,rank_result AS
(
SELECT T.product_id,
T.rack,
T.stock,
R.freq,
U.num_users,
ROW_NUMBER() OVER(PARTITION BY T.product_id ORDER BY R.freq DESC, T.stock DESC) AS RN
FROM tbl_counting T
LEFT JOIN uses_count U
ON T.product_id = U.product_id
AND CASE WHEN U.num_users >= 3 THEN TRUE ELSE FALSE END
LEFT JOIN rack_count R
ON T.product_id = R.product_id
AND CASE WHEN R.freq >= 2 THEN TRUE ELSE FALSE END
)
SELECT product_id,
stock,
CASE WHEN freq IS NULL THEN 'Unknown' ELSE rack END AS rack
FROM rank_result
WHERE RN = 1) R
ON P.Id = R.product_id
SET P.stock = R.stock,
P.rack = R.rack

Get last update time in a status history table for a given status

I've this table
order_status_history, I insert a row everytime an order is updated, and let's say a table orders with only the column id:
id
order_id
order_status_id
updated_at
1
121
ready
2021-12-30 10:00:00
2
121
shipped
2021-12-30 10:01:00
3
121
ready
2021-12-30 10:02:00
4
121
shipped
2021-12-30 10:03:00
5
121
delivered
2021-12-30 10:04:00
I need to get the latest updated_at value for all the orders (DISTINCT) that have been shipped as last status, or delivered if for any reason there is no shipped status .
I wrote this query, but I get the last updated_at value of the delivered status. In the example case I need the updated_at as 10:03, but I get the 10:04 one.
SELECT o.id,
osh.max_updated_at AS updatedAt
FROM orders o
JOIN ( SELECT order_id,
Max(updatedat) AS max_updated_at
FROM order_status_history
WHERE order_status_id IN('shipped','delivered')
GROUP BY order_id
) AS osh ON osh.order_id = o.id
AND ( SELECT order_status_id
FROM order_status_history
WHERE order_id = o.id
ORDER BY updatedat DESC
LIMIT 1
) IN('shipped','delivered')
Try this one with subquery
SELECT id, (
SELECT h.updated_at
FROM order_status_history h
WHERE h.order_id = o.id
AND h.order_status_id IN ('shipped','delivered')
AND NOT EXISTS (
SELECT 1
FROM order_status_history k
WHERE k.order_id = h.order_id
AND k.updated_at > h.updated_at
AND k.order_status_id NOT IN ('shipped','delivered')
)
ORDER BY CASE WHEN order_status_id = 'shipped' THEN 1 ELSE 2 END ASC, updated_at DESC
LIMIT 1
) AS updatedAt
FROM orders o
Subquery works if you select a single column
This is another option for MySQL8+
WITH ordered AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY order_id ORDER BY CASE WHEN order_status_id = 'shipped' THEN 1 ELSE 2 END ASC, updated_at DESC) rn
FROM order_status_history
WHERE order_status_id IN ('shipped','delivered')
)
SELECT o.id, h.*
FROM orders o
LEFT JOIN ordered h ON o.id = h.order_id
AND h.rn = 1 AND NOT EXISTS (
SELECT 1
FROM order_status_history k
WHERE k.order_id = h.order_id
AND k.updated_at > h.updated_at
AND k.order_status_id NOT IN ('shipped','delivered')
)

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

MySQL - Display null column from child table if all values are not distinct

I have the following tables, for example:
invoices
ID Name
1 A
2 B
3 C
4 D
5 E
transactions
ID Invoice_ID User_ID
1 1 10
2 1 10
3 1 10
4 2 30
5 3 20
6 3 40
7 2 30
8 2 30
9 4 40
10 3 50
Now I want to make a select that will pull the invoices and the user_id from the related transactions, but of course if I do that I won't get all the ids, since they may be distinct but there will be only one column for that. What I want to do is that if there are distinct User_ids, I will display a pre-defined text in the column instead of the actual result.
select invoices.id, invoices.name, transactions.user_id(if there are distinct user_ids -> return null)
from invoices
left join transactions on invoices.id = transactions.invoice_id
and then this would be the result
ID Name User_ID
1 A 10
2 B 30
3 C null
4 D 40
5 E null
Is this possible?
You can do the following :
select
invoices.id,
invoices.name,
IF (
(SELECT COUNT(DISTINCT user_id) FROM transactions WHERE transactions.invoice_id = invoices.id) = 1,
(SELECT MAX(user_id) FROM transactions WHERE transactions.invoice_id = invoices.id),
null
) AS user_id
from invoices
Or, alternatively, you can use the GROUP_CONCAT function to output a comma-separated list of users for each invoice. It is not exactly what you asked, but maybe in fact it will be more useful :
select
invoices.id,
invoices.name,
GROUP_CONCAT(DISTINCT transactions.user_id SEPARATOR ',') AS user_ids
from invoices
left join transactions on invoices.id = transactions.invoice_id
group by invoices.id
Try somethingh like:
select i.id, i.name, t.user_id
from invoices i left join
(
select invoice_ID, User_ID
from transactions
group by invoice_ID
having count(invoice_ID)=1
) t on i.id=t.invoice_id
SQL fiddle
You could list all the transactions that have multiple user ids, like this:
select invoices.id, invoices.name, null
from invoices
left join transactions on invoices.id = transactions.invoice_id having count(distinct transactions.user_id) > 1
Also, I think this CASE might suit your needs here:
select invoices.id, invoices.name,
case when count(distinct transactions.user_id) > 1 then null else transactions.user_id end
from invoices
left join transactions on invoices.id = transactions.invoice_id
group by invoices.id
although, I'm not sure this is syntactically correct

Select last N rows following a condition

I've got a database table with logs which has 3 columns:
date | status | projectId
status can be either 0 or 1, primary key is on date and projectID
I'm trying to find out how many times a projectID had status 0 since the last time it was 1.
so if there would be only one projectId
date | status | projectId
1 0 3
2 0 3
3 1 3
4 1 3
5 0 3
6 0 3
this should return 2 (row 5 and 6 are 0 and row 4 is 1)
The thing that makes it hard for me is that I have to maintain the order of date. What would be a good way to tackle such problems, and this one in particular?
Here is how you would do it for one project:
select count(*)
from logs l
where status = 0 and
projectid = 3 and
date > (select max(date) from logs where projectid = 3 and status = 1)
Here is how you would do it for all projects:
select l.projectId, count(l1.projectId)
from logs l left outer join
(select projectId, max(date) as maxdate
from logs
where status = 1
group by projectId
) l1
on l.projectId = l1.projectId and
l.date > l1.date and
l.status = 0
group by l.projectId;
here you have an option in just one select.
http://sqlfiddle.com/#!2/6ce87/11
select *
from logs
where status=0 and date > (select date from logs where status=1 order by date desc limit 1)
Here's one way to get the result for all project_id:
SELECT m.project_id
, COUNT(1) AS mycount
FROM ( SELECT l.project_id
, MAX(l.date) AS latest_date
FROM mytable l
WHERE l.status = 1
) m
JOIN mytable t
ON t.project_id = m.project_id
AND t.date > m.latest_date
AND t.status = 0
If you need only a subset of project_id, the predicate should be added to the WHERE clause in the inline view query:
WHERE l.status = 1
AND l.project_id IN (3,5,7)
EDIT
That query does not return a row if there is no status=0 row after the latest status=1 row. To return a zero count, this could be done with an outer join.
SELECT m.project_id
, COUNT(t.status) AS mycount
FROM ( SELECT l.project_id
, MAX(l.date) AS latest_date
FROM mytable l
WHERE l.status = 1
AND l.project_id IN (3)
) m
LEFT
JOIN mytable t
ON t.project_id = m.project_id
AND t.date > m.latest_date
AND t.status = 0
For optimum performance, the statement could make use of an index with leading columns of project_id and date (in that order) and including the status column, e.g.
ON mytable (`project_id`,`date`,`status`)