Select last Order state from Orders - mysql

I have tables:
orders:
id_order id_customer
1 1
2 2
3 1
orders_history
id_history id_order id_order_state date_add
1 1 1 2010-01-01 00:00:00
2 1 2 2010-01-02 00:00:00
3 1 3 2010-01-03 00:00:00
4 2 2 2010-05-01 00:00:00
5 2 3 2011-05-02 00:00:00
6 3 1 2011-05-03 00:00:00
7 3 2 2011-06-01 00:00:00
order_state
id_order_state name
1 New
2 Sent
3 Rejected
4 ...
How to get all order_id's where last id_order_state of that order (by last I mean this with MAX(id_history) or MAX(date_add)) is not equal 1 or 3?

select oh.id_history, oh.id_order, oh.id_order_state, oh.date_add
from (
select id_order, max(date_add) as MaxDate
from orders_history
where id_order_state not in (1, 3)
group by id_order
) ohm
inner join orders_history oh on ohm.id_order = oh.id_order
and ohm.MaxDate = oh.date_add

I think what he's after is what orders are complete... ie their final status, not those that are exclusive of the 1 and 3 specifically. The first pre-query should be the max ID regardless of the status code
select
orders.*
from
( select oh.id_order,
max( oh.id_history ) LastID_HistoryPerOrder
from
orders_history oh
group by
oh.id_order ) PreQuery
join orders_history oh2
on PreQuery.ID_Order = oh2.id_order
AND PreQuery.LastID_HistoryPerOrder = oh2.id_history
AND NOT OH2.id_order_state IN (1, 3) <<== THIS ELIMINATES 1's & 3's from result set
join Orders <<= NOW, anything left after above ^ is joined to orders
on PreQuery.ID_Order = Orders.ID_Order
Just to re-show YOUR data... I've marked the last SEQUENCE (ID_History) per ORDER... This is what the PREQUERY is going to return...
id_history id_order id_order_state date_add
1 1 1 2010-01-01 00:00:00
2 1 2 2010-01-02 00:00:00
**3 1 3 2010-01-03 00:00:00
4 2 2 2010-05-01 00:00:00
**5 2 3 2011-05-02 00:00:00
6 3 1 2011-05-03 00:00:00
**7 3 2 2011-06-01 00:00:00
The "PreQuery" will result with the following subset
ID_Order LastID_HistoryPerOrder (ID_History)
1 3 (state=3) THIS ONE WILL BE SKIPPED IN FINAL RESULT
2 5 (state=3) THIS ONE WILL BE SKIPPED IN FINAL RESULT
3 7 (state=2)
Now, the result of this is then re-joined back to order history on just these two elements... yet adds the criteria to EXCLUDE the 1,3 entries for "order state".
In this case,
1 would be rejected as its state = 3 (sequence #3),
2 would be rejected since its last history is state = 3 (sequence #5).
3 would be INCLUDED since its state = 2 (sequence #7)
Finally, all that joined to the orders will result with ONE ID, and nicely match up with the orders table on the Order_ID alone and get the desired results.

Another possible solution:
SELECT DISTINCT
id_order
FROM
Orders_History OH1
LEFT OUTER JOIN Orders_History OH2 ON
OH2.id_order = OH1.id_order AND
OH2.is_order_state IN (1, 3) AND
OH2.date_add >= OH1.date_add
WHERE
OH2.id_order IS NULL

I'm using "answer for my question" because I need to post results of your queries. So.
Unfortunately not all of your answers guys works. Let's prepare test environment:
CREATE TABLE `order_history` (
`id_order_history` int(11) NOT NULL AUTO_INCREMENT,
`id_order` int(11) NOT NULL,
`id_order_state` int(11) NOT NULL,
`date_add` datetime NOT NULL,
PRIMARY KEY (`id_order_history`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin2;
CREATE TABLE `orders` (
`id_order` int(11) NOT NULL AUTO_INCREMENT,
`id_customer` int(11) DEFAULT NULL,
PRIMARY KEY (`id_order`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=latin2;
INSERT INTO `order_history`
(`id_order_history`, `id_order`, `id_order_state`, `date_add`) VALUES
(1,1,1,'2011-01-01 00:00:00'),
(2,1,2,'2011-01-01 00:10:00'),
(3,1,3,'2011-01-01 00:20:00'),
(4,2,1,'2011-02-01 00:00:00'),
(5,2,2,'2011-02-01 00:25:01'),
(6,2,3,'2011-02-01 00:25:59'),
(7,3,1,'2011-03-01 00:00:01'),
(8,3,2,'2011-03-01 00:00:02'),
(9,3,3,'2011-03-01 00:01:00'),
(10,3,2,'2011-03-02 00:00:01');
COMMIT;
INSERT INTO `orders` (`id_order`, `id_customer`) VALUES
(1,1),
(2,2),
(3,3),
(4,4),
(5,5),
(6,6),
(7,7);
COMMIT;
Now, lets select Last/Max State for each Order, so let's run simple query:
select id_order, max(date_add) as MaxDate
from `order_history`
group by id_order
this gives us PROPER results, no rocket science right now:
id_order MaxDate
---------+-------------------
1 2011-01-01 00:20:00 //last order_state=3
2 2011-02-01 00:25:59 //last order_state=3
3 2011-03-02 00:00:01 //last order_state=2
Now for simplicity, lest change our queries to get Orders where Last State is not equal 3.
We're expecting to get one row result with id_order = 3.
So let's test our queries:
QUERY 1 made by RedFilter:
select oh.id_order, oh.id_order_state, oh.date_add
from (
select id_order, max(date_add) as MaxDate
from `order_history`
where id_order_state not in (3)
group by id_order
) ohm
inner join `order_history` oh on ohm.id_order = oh.id_order
and ohm.MaxDate = oh.date_add
Result:
id_order id_order_state date_add
-------------------------------------------------
1 2 2011-01-01 00:10:00
2 2 2011-02-01 00:25:01
3 2 2011-03-02 00:00:01
So it's not true
QUERY 2 made by Tom H.:
SELECT DISTINCT OH1.id_order
FROM order_history OH1
LEFT OUTER JOIN order_history OH2 ON
OH2.id_order = OH1.id_order AND
OH2.id_order_state NOT IN (3) AND
OH2.`id_order_history` >= OH1.`id_order_history`
WHERE
OH2.id_order IS NULL
Result:
id_order
--------
1
2
So it's not true
Any suggestions appreciated.
EDIT
Thanks to Andriy M. comment we have proper solution. It's a modification of Tom H. query all should looks as follow:
SELECT DISTINCT
OH1.id_order
FROM
order_history OH1
LEFT OUTER JOIN order_history OH2 ON
OH2.id_order = OH1.id_order
AND OH2.date_add > OH1.date_add
WHERE OH1.id_order_state NOT IN (3) AND OH2.id_order IS NULL
EDIT 2:
QUERY 3 made by DRapp:
select
distinct orders.`id_order`
from
( select oh.id_order,
max( oh.id_order_history ) LastID_HistoryPerOrder
from
order_history oh
group by
oh.id_order ) PreQuery
join order_history oh2
on PreQuery.id_order = oh2.id_order
AND PreQuery.LastID_HistoryPerOrder = oh2.id_order_history
AND NOT oh2.id_order_state IN (1,3)
join orders
on PreQuery.id_order = orders.id_order
Result:
id_order
--------
3
So it's finally true

Related

Select only unique ids row of table with different values in Descending order

I'm trying to get data that have the same medicine_id and unique insurance_id and last inserted row. Put Group by and Order by but in that got random data not last inserted.
I tried this code but got not last inserted data
SELECT
`m1`.`*`
FROM
(
`pricings` `m1`
LEFT JOIN `pricings` `m2` ON
(
(
(
`m1`.`medicine_id` = `m2`.`medicine_id`
)
)
)
)
WHERE m1.medicine_id = 2
group BY m1.insurance_id DESC
ORDER BY m1.created_at;
Here are the total rows.
This is a full table
id
medicine_id
insurance_id
created_at
4311
2
1
2021-04-12 16:05:07
4766
2
1
2022-01-15 11:56:06
4767
2
38
2021-05-12 08:17:11
7177
2
38
2022-03-30 10:14:11
4313
2
39
2021-04-12 16:05:46
4768
2
39
2021-05-12 08:17:30
1356
2
40
2020-11-02 11:25:43
3764
2
40
2021-03-08 15:42:16
4769
2
40
2021-05-12 08:17:44
And I want to like this
id
medicine_id
insurance_id
created_at
4766
2
1
2022-01-15 11:56:06
4768
2
39
2021-05-12 08:17:30
4769
2
40
2021-05-12 08:17:44
7177
2
38
2022-03-30 10:14:11
MySQL 5.x: Use a sub-query to find the max created_at value per group, then join that on the source table to identify the row it was from.
SELECT
p.`*`
FROM
`pricings` p
INNER JOIN
(
SELECT
`medicine_id`,
`insurance_id`,
MAX(created_at) AS `created_at`
FROM
`pricings`
GROUP BY
`medicine_id`,
`insurance_id`
)
p_max
ON p.`medicine_id` = p_max.`medicine_id`
AND p.`insurance_id` = p_max.`insurance_id`
AND p.`created_at` = p_max.`created_at`
WHERE
p.`medicine_id` = 2
ORDER BY
p.`created_at`;
MySQL 8: Use ROW_NUMBER() to enumerate each group, then pick the first row from each group.
SELECT
p.`*`
FROM
`pricings` p
FROM
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY `medicine_id`,
`insurance_id`
ORDER BY `created_at` DESC
)
AS `row_id`
FROM
`pricings`
)
p
WHERE
p.`medicine_id` = 2
AND p.`row_id` = 1
ORDER BY
p.`created_at`;
Adding it as an answer as well. I have not tested it, just fix the formating to work with whatever version of databse you are working with and let me know of the results.
SELECT m1.id , m1.Insurance_id , m1.medicine_id , max(m1,created_at)
FROM (
`pricings` `m1` LEFT JOIN `pricings` `m2` ON `m1`.`medicine_id` = `m2`.`medicine_id`
)
WHERE m1.medicine_id = 2 and m1.insurance_id in (1,39,40,38)
GROUP BY m1.insurance_id DESC
ORDER BY m1.created_at;
Edit. I also removed the 6 extra parenthesis, I don't see how they could be of any use

How do I get the MAX date of foreign table in MySQL and return only the row with that data without mixing records?

I have a few MySQL tables from which I need to JOIN and return data. The return data must show only one row for one of the JOINed tables, but MySQL mixes the rows.
I have tried different methods using subqueries and normal JOIN with GROUP but the results remain pretty much the same.
Example table structure
suppliers
id name ...
1 ACME ...
2 EMCA ...
3 ORG ...`
ratings
id supplier_id rating expiry_date report_file
1 1 5.0 2017-01-31 a.pdf
3 1 7.9 2019-06-30 c.pdf
4 2 5.0 2016-01-31 d.pdf
5 2 2.0 2018-11-30 g.pdf
6 245 9.5 2009-03-31 p.pdf
spends
id report_id supplier_id amount
1 1 1 150.00
2 1 2 100.00
3 1 245 200.00
Here are example queries I have tried to resolve this and return the correct dataset with no luck.
SELECT
reports.id,
suppliers.id AS supplier_id,
suppliers.name,
...
spends.amount,
...
ratings.rating,
ratings.report_file,
ratings.expiry_date
FROM reports
INNER JOIN spends ON reports.id=spends.report_id
INNER JOIN suppliers ON spends.supplier_id=suppliers.id
LEFT JOIN (
SELECT id,
level,
report_file,
supplier_id,
MAX(expiry_date) AS expiry_date
FROM ratings
GROUP BY supplier_id
) ratings ON (ratings.supplier_id=suppliers.id
AND ratings.expiry_date >= reports.period_start)
...
WHERE reports.id = 1
GROUP BY spends.id
ORDER BY spends.amount DESC
Another query
SELECT
reports.id,
suppliers.id AS supplier_id,
suppliers.name,
...
spends.amount,
...
ratings.rating,
ratings.report_file,
MAX(ratings.expiry_date) AS expiry_date
FROM reports
INNER JOIN spends ON reports.id=spends.report_id
INNER JOIN suppliers ON spends.supplier_id=suppliers.id
LEFT JOIN ratings ON (ratings.supplier_id=suppliers.id
AND ratings.expiry_date >= reports.period_start)
...
WHERE reports.id = 1
GROUP BY spends.id
ORDER BY spends.amount DESC
I expect the results to be
id supplier_id name amount rating report_file expiry_date
1 1 ACME 150.00 7.9 c.pdf 2019-06-30
1 2 EMCA 100.00 2.0 g.pf 2018-11-30
1 245 MACE 200.00 null null```
However, the actual output is
```sql
id supplier_id name amount rating report_file expiry_date
1 1 ACME 150.00 5.0 a.pdf 2019-06-30
1 2 EMCA 100.00 5.0 d.pf 2018-11-30
1 245 MACE 200.00 null null
Please could anyone advise how I can fix this.
try a query like this:
SELECT
reports.id,
suppliers.id AS supplier_id,
suppliers.name,
...
spends.amount,
...
r.rating,
r.report_file,
r.expiry_date
FROM reports
INNER JOIN spends ON reports.id=spends.report_id
INNER JOIN suppliers ON spends.supplier_id=suppliers.id
LEFT JOIN ratings r ON r.supplier_id=suppliers.id
AND r.`expire_date` = (
SELECT MAX(`expire_date`) FROM ratings WHERE supplier_id = supplier.id GROUP BY supplier_id)
) as maxdate
...
WHERE reports.id = 1
GROUP BY spends.id
ORDER BY spends.amount DESC

MYSQL: View statement produces incorrect SUM totals

I have 3 tables. A table for product prices, invoiced products, and ordered products. I am trying to create a view that joins these. I want to output the product prices with a total of invoiced products and a total of ordered products.
products_price
id season_id product_id product_price
1 1 1 3.99
2 1 2 6.99
3 1 3 5.99
4 1 4 5.99
....
invoices_products
id season_id invoice_id product_id piece_qty
1 1 1 1 1600
2 1 2 2 3200
3 1 3 2 200
4 1 4 1 120
....
orders_products
id season_id order_id product_id piece_qty
1 1 1 1 160
2 1 2 1 40
3 1 2 2 20
4 1 3 2 10
....
Here are a few queries from the View statements I've tried so far.
This query gives me everything I want. The View's output is perfect but the SUM() for the first 2 rows is off. total_invoice_product is double for row 1 and 2. total_order_productis 4x for row 1 and 3x for row 2.
Statement 1:
SELECT
`t1`.`id` AS `id`,
`t1`.`season_id` AS `season_id`,
`t1`.`product_id` AS `product_id`,
`t1`.`product_piece_price` AS `product_piece_price`,
SUM(`t2`.`piece_qty`) AS `total_invoice_product`,
SUM(`t3`.`piece_qty`) AS `total_order_product`
FROM
((`products_price` `t1`
LEFT JOIN `invoices_products` `t2` ON (((`t2`.`product_id` = `t1`.`product_id`)
AND (`t2`.`season_id` = `t1`.`season_id`))))
LEFT JOIN `orders_products` `t3` ON (((`t3`.`product_id` = `t1`.`product_id`)
AND (`t3`.`season_id` = `t1`.`season_id`))))
GROUP BY `t1`.`season_id` , `t1`.`product_id`
This query gives me the output that I expect. Its not the full output I want but its correct for the statement. The SUM() totals are off on this one as well.
Statement 2:
SELECT
`t1`.`id` AS `id`,
`t1`.`season_id` AS `season_id`,
`t1`.`product_id` AS `product_id`,
`t1`.`product_price` AS `product_price`,
SUM(`t2`.`piece_qty`) AS `total_invoice_product`,
SUM(`t3`.`piece_qty`) AS `total_order_product`
FROM
((`products_price` `t1`
LEFT JOIN `invoices_products` `t2` ON ((`t2`.`product_id` = `t1`.`product_id`)))
LEFT JOIN `orders_products` `t3` ON ((`t3`.`product_id` = `t1`.`product_id`)))
WHERE
((`t2`.`season_id` = `t1`.`season_id`)
AND (`t2`.`product_id` = `t1`.`product_id`))
GROUP BY `t1`.`season_id` , `t1`.`product_id`
the output I want
id season_id product_id product_price total_invoice total_order
1 1 1 3.99 1720 200
2 1 2 6.99 3400 30
3 1 3 5.99 576
4 1 4 5.99 800
output received for statement 1
id season_id product_id product_price total_invoice total_order
1 1 1 3.99 3440 800
2 1 2 6.99 6800 90
3 1 3 5.99 576
4 1 4 5.99 800
output received for statement 2
id season_id product_id product_price total_invoice total_order
1 1 1 3.99 3440 800
2 1 2 6.99 6800 90
I can build a query like below and it works perfect. I get the exact output I need but this code does not work as a view. I get this error: ERROR 1349: View's SELECT contains a subquery in the FROM clause SQL Statement
Perfect Query but will not work as a view
SELECT
products_price.id,
products_price.season_id,
products_price.product_id,
products_price.product_price,
invoices_grouped.total_invoice_product,
orders_grouped.total_order_product
FROM
products_price
LEFT JOIN
(SELECT
invoices_products.product_id,
invoices_products.season_id,
SUM(invoices_products.piece_qty) AS total_invoice_product
FROM
invoices_products
GROUP BY
invoices_products.product_id) AS invoices_grouped
ON
invoices_grouped.product_id = products_price.product_id
AND
invoices_grouped.season_id = products_price.season_id
LEFT JOIN
(SELECT
orders_products.product_id,
orders_products.season_id,
SUM(orders_products.piece_qty) AS total_order_product
FROM
orders_products
GROUP BY
orders_products.product_id) AS orders_grouped
ON
orders_grouped.product_id = products_price.product_id
AND
orders_grouped.season_id = products_price.season_id
What I need
I've tried several other statements. They either got worse results or the same. Can someone help me get Statement 1 working with a proper SUM?
Edit 1 for a question
The information that this view provides will be called upon a lot. The products_price and invcoices_products tables will not be changed that often. orders_products will be changed a lot. If 2 views are required, would it be more efficient to use the "Perfect" query above or use 2 views?
Edit 2 for another query
Here is another query from my view statement. This query is part of Statement 1 shown above. This query works perfect but it is not complete. I need the second SUM column. When you add the second LEFT JOIN it breaks the SUM totals.
SELECT
`t1`.`id` AS `id`,
`t1`.`season_id` AS `season_id`,
`t1`.`product_id` AS `product_id`,
`t1`.`product_piece_price` AS `product_piece_price`,
SUM(`t2`.`piece_qty`) AS `total_invoice_product`
FROM
(`products_price` `t1`
LEFT JOIN `invoices_products` `t2` ON (((`t2`.`product_id` = `t1`.`product_id`)
AND (`t2`.`season_id` = `t1`.`season_id`))))
GROUP BY `t1`.`season_id` , `t1`.`product_id`
output
id season_id product_id product_price total_invoice
1 1 1 3.99 1720
2 1 2 6.99 3400
3 1 3 5.99 576
4 1 4 5.99 800
Well, MySql has some limitations, so you will need to create 2 views for subqueries and use them:
create view viewInvoices
as
select season_id, product_id, sum(piece_qty) pq
from invoices_products group by season_id, product_id
create view viewOrders
as
select season_id, product_id, sum(piece_qty) pq
from orders_products group by season_id, product_id
select pp.id,
pp.season_id,
pp.product_id,
pp.product_price,
i.pq as total_invoice,
o.pq as total_order
from products_price pp
left join viewInvoices as i
on pp.season_id = i.season_id and pp.product_id = i.product_id
left join viewOrders as o
on pp.season_id = o.season_id and pp.product_id = o.product_id
Try this query
SELECT * from products_price as a left join (select product_id, sum(piece_qty)total_invoices from invoices_products group by product_id) as b on a.product_id=b.product_id left join (select product_id, sum(piece_qty) as total_order from orders_products group by product_id) as c on a.product_id=c.product_id

MYSQL select 2 diffrent Fields with same value from different lines

I have a table:
ID APARTMENT_ID CHECK_IN CEHCK_OUT
1 2 2014-18-07 2014-18-09
2 2 2015-01-01 2014-03-02
3 3 2014-08-01 2014-09-01
4 3 2014-09-01 2014-12-09
5 4 2014-18-07 2014-18-09
I want to get the ID, APARTMENT_ID and CHECK_IN , CEHCK_OUT . where there is on the same date in different ID.
on this table the query will bring the result of
APARTMENT_ID => 3
ID => 3, 4
CHECK_IN CEHCK_OUT => 2014-09-01.
i tried something like
SELECT * FROM `orders`
WHERE `ID` != `ID` and `CHECK_IN`=`CEHCK_OUT`
but its not the right way , 0 results.
thanks to anyone that could help me.
You have to use a self-join, otherwise you're just comparing fields in the same row:
SELECT *
FROM orders o1
JOIN orders o2 ON o1.id != o2.id AND o1.check_in = o2.check_out

Select join on subquery from multiple table

Existing code i have for my select query
Table named material:
mat_id mat_name supplier_id stock_in stock_released Balance date
1 alloy 4 30 0 30 feb13
2 steel 2 15 0 15 feb13
3 alloy 2 0 3 15 feb14
SELECT m.`mat_id`, m.`mat_name`, m.`stock_in`, m.`stock_released`,
(select sum(stock_in) - sum(stock_released)
from material m2
where m2.mat_name = m.mat_name and
m2.mat_id <= m.mat_id
) as balance,
m.`date`
FROM `material` m
ORDER BY m.`mat_id` ASC;
How can I add in my query the supplier name from supplier table? How to Join this?
sup_id sup_name
2 rain
4 george
Try this SQL:
SELECT m.`mat_id`, m.`mat_name`, m.`stock_in`, m.`stock_released`,
(select sum(stock_in) - sum(stock_released)
from material m2
where m2.mat_name = m.mat_name and
m2.mat_id <= m.mat_id
) as balance,
m.`date`,
s.`sup_name`
FROM `material` m
LEFT JOIN `supplier` s on s.sup_id = m.supplier_id
ORDER BY m.`mat_id` ASC;