How to join tables using the highest value? - mysql

I'm joining two tables based on id_order and it's one to many relation. My query looks like this:
SELECT
ps_order_detail.product_name,
ps_order_history.id_order_state
FROM ps_order_detail JOIN
ps_order_history using (id_order)
The problem is that it returns all of the id_order_state values, and I want only the highest one. I tried doing max(ps_order_history.id_order_state) but it returns only one record with the highest value, and I want to have the highest value for each id_order. How can I do it?

You need to use aggregate function max with a group by clause. I am not sure which flavor of SQL server you are using. For MS SQL Server following query will do what you want.
SELECT product_name, MAX(id_order_state)
FROM ps_order_detail
GROUP BY product_name

One approach is to generate a subset of the max ID_ORDER_STATE per order and add it to the joins.
SELECT ps_order_detail.product_name
, ps_order_history.id_order_state
FROM ps_order_detail
INNER JOIN ps_order_history using (id_order)
INNER JOIN (SELECT max(ID_ORDER_STATE) MOS, ID_ORDER
FROM ps_order_history
GROUP BY ID_ORDER) Z
on Z.MOS = ps_order_history.id_order_state
and Z.ID_ORDER = ps_order_history.id_order
This approach allows you to return the other data from history related to the max record; but it's overkill if all you need is the max order state for each product name
Other approaches involve using cross apply or analytical functions but mySQL doesn't support those approaches.
I suppose you could use an exists and correlate subquery as well... but I find the above approach clean to read.

Related

How to fetch data from database without using a sub-query in following scenario?

I had to get the town name and state from database which deployed the first reciever(a device to sense sharks). I used the following query with a subquery computing MIN of deployed dates:
SELECT t.town,t.state,r.deployed FROM receiver as r
inner join township as t
on r.location=t.tid
where deployed=(select min(deployed) from receiver);
I got following result which is correct:
Result from query
I tried to do the same thing by using having clause i.e without the subquery as following:
SELECT t.town,t.state,r.deployed FROM receiver as r
inner join township as t
on r.location=t.tid
having r.deployed=min(deployed);
But I got no rows from this query. My question is whats the problem with second query? Am I not using the min function properly in the query? What's the right way to write this query without subquery so that I can get the result just as I got from the former query?
If you don't care about ties, we can use a limit query here:
SELECT t.town, t.state, r.deployed
FROM receiver AS r
INNER JOIN township AS t
ON r.location = t.tid
ORDER BY r.deployed
LIMIT 1;
If you do want all ties, then RANK() is another option, but that would require a subquery:
WITH cte AS (
SELECT t.town, t.state, r.deployed, RANK() OVER (ORDER BY r.deployed) rnk
FROM receiver AS r
INNER JOIN township AS t
ON r.location = t.tid
)
SELECT town, state, deployed
FROM cte
WHERE rnk = 1;
Note that some other databases (besides MySQL) do support a QUALIFY clause, which actually would let us use RANK() without a formal subquery:
SELECT t.town, t.state, r.deployed
FROM receiver AS r
INNER JOIN township AS t
ON r.location = t.tid
QUALIFY RANK() OVER (ORDER BY r.deployed) = 1;
You cannot use the field deployed both as an aggregate MIN(deployed) AND a non-aggregate r.deployed at the same time. The first query works because the subquery searches and finds ONE value, and then the main query compares ALL values, one by one, to that one found value to find the right one. These are two separate actions, both (in theory) going through their individual entire datasets. You cannot combine these two very different actions in one query without subquery.

sql SELECT query for 3 tables

I have 3 tables:
1. products(product_id,name)
2. orders(id,order_id,product_id)
3. factors(id,order_id,date)
I want to retrieve product names(products.name) where have similar order_id on a date in two last tables.
I use this query for this purpose:
select products.name
from products
WHERE products.product_id ~IN
(
SELECT distinct orders.product_id FROM orders WHERE
order_id IN (select order_id FROM factors WHERE
factors.datex ='2017-04-29') GROUP BY product_id
)
but no result. where is my mistake? how can I resolve that? thanks
Your query should be fine. I am rewriting it to make a few changes to the structure, but not the logic (this makes it easier for me to understand the query):
select p.name
from products p
where p.product_id in (select o.product_id
from orders o
where o.order_id in (select f.order_id
from factors f
where f.datex = '2017-04-29'
)
) ;
Notes on the changes:
When using multiple tables in a query, always qualify the column names.
Use table aliases. They make queries easier to write and to read.
SELECT DISTINCT and GROUP BY are unnecessary in IN subqueries. The logic of IN already handles (i.e. ignores) duplicates. And by explicitly including the operations, you run the risk of a less efficient query plan.
Why might your query not work?
factors.datex has a time component. If so, then this will work date(f.datex) = '2017-04-29'.
There are no factors on that date.
There are no orders that match factors on that date.
There are no products in the orders that match the factors on that date.
In factors table column name is date so it should be -
factors.date ='2017-04-29'
You have written -
factors.datex ='2017-04-29'

Mysql how can I select all orders with both attributes

I have a table call orders with the ordernumber and another table call order_detail with ordernumber and product number.
I tried to query:
Select a.* from orders as a
join
order_detail as b on a.ordernum = b.ordernum
where b.prodnum ='Bike28B' or b.prodnum = 'ridinggloves'
group by ordernum;
It will give the the result with orders having Bike28B but not the result with the order have both attributes.
You have unintentionally stumbled upon mysql's quirky grouping implementation, which does non-standard things. Try listing all columns from
select a.*
from orders as a
join order_detail as b
on a.ordernum = b.ordernum
and b.prodnum in ('Bike28B', 'ridinggloves')
group by <list every column in order>
having count(*) = 2
The having clause ensures both child rows were found.
Note also the use of in (...) instead of or, which will have a much better chance if using an index (if one exists) on that column. I also moved that condition into the join clause, because it's a join condition (although it would work in both places).

Sql: select count and also many other fields in one query

I am using Sql Server 2008 r2.
I have the following tables: Family, FamilyChild, Scale, Allowance.
I need to retrieve the number of children per family in order to select the appropriate value in the Scale table and then insert this value with many other fields (from these or other tables) in the Allowance table.
I need to do this for each family row in the family table.
How can I do this as simple as possible ?
Thanks.
Assuming that every row on FamilyChild represents one child:
SELECT F.FamilyID,
COUNT(*) NumberOfChildren
FROM Family F
LEFT JOIN FamilyChild FC
ON F.FamilyID = FC.FamilyID
GROUP BY F.FamilyID
Without the fields of the tables (and thoses needed to join the tables, you will not have a very appropriate answer. All I can say is: use a count() with the group by operator:
select f.family_name, count(c.children_id)
from family f inner join familyChild c on f.family_id = c.family_id
Then you can join this with the scale table or use it in and EXISTS subquery to filter the scale table and do your insert.
while using aggregate functions like count,max you should use group by clause with select statement and the columns which are not participated in aggregate function should be group by clause....for example
select f.family_name, count(c.children_id)
from family f inner join familyChild c on f.family_id = c.family_id
group by f.family_name

Is this possible in a single mysql query?

I have two tables with a one to many relationship, offer and offer_rows
I want to fetch multiple offers with their content rows. That on it's own is not difficult, I just use an
INNER JOIN on offer.offer_id = offer_rows.offer_id
However, the offer_rows table contains a field called revision and the query needs to always fetch all the rows with the highest revision number. Is this possible with a single query?
I realize I could change the database design, by adding a third table called offer_revision, I could join this table with a select condition to fetch the latest revision number and then connect this table to the rows. This however would take considerable refactoring so I only want to do it if I have to.
I also want to do this with a direct query - no stored procedures.
Of course it is possible:
SELECT o.*, r.revision, r.something_else
FROM offer o,
offer_rows r
WHERE o.offer_id = r.offer_id
AND r.revision = (
SELECT max(revision)
FROM offer_rows
WHERE offer_id = o.offer_id
)
You can select all the rows from offer_rows with the MAX(revision) and then JOIN the offer table (no nested query will be required):
SELECT *, MAX(revision) as latest_revision
FROM offer_rows or
INNER JOIN offer o USING( offer_id )
GROUP BY offer_id
Yes this is possible with a single query. You could have a subquery that get's the highest revision in the WHERE clause.
I've used the following comparison to get a latest version entry:
AND `outer`.`version` = (
SELECT MAX( `inner`.`version` )
FROM `content` `inner`
WHERE `inner`.`id` = `outer`.`id`
AND `inner`.`language` = `outer`.`language`
)