60

OK I tried googling for an answer like crazy, but I couldn't resolve this, so I hope someone will be able to help.

Let's say I have a table of users, very simple table:

id | userName
3    Michael
4    Mike
5    George

and I have another table of their cars and their prices.

id | belongsToUser | carPrice
1    4               5000
2    4               6000
3    4               8000

Now what I need to do is something like this (feel free to rewrite):

   SELECT
      `userName`,
      `carPrice`
   FROM `users`
   LEFT JOIN `cars`
   ON cars.belongsToUser=users.id
   WHERE `id`='4'

Which returns:

Mike | 5000

But I need the most expensive car of a certain user, not the first entry found.

So question: How do I set the LEFT JOIN table to be ordered by carPrice, DESC ?

William Entriken
  • 37,208
  • 23
  • 149
  • 195
Frantisek
  • 7,485
  • 15
  • 59
  • 102
  • 1
    Is should return all 3 records, how come its returning only 1 ... What are we missing? – Nitin Midha Mar 18 '11 at 17:23
  • 1
    You do not show the entire query, it should have returned three results – Itay Moav -Malimovka Mar 18 '11 at 17:25
  • When you say "ordered by carPrice DESC" do you mean you want all the matching cars, ordered by price? Or do you mean you want the join to only get one matching car, and that car be the most expesnive one? – MatBailie Mar 18 '11 at 17:27

5 Answers5

71

Try using MAX with a GROUP BY.

SELECT u.userName, MAX(c.carPrice)
FROM users u
    LEFT JOIN cars c ON u.id = c.belongsToUser
WHERE u.id = 4;
GROUP BY u.userName;

Further information on GROUP BY

The group by clause is used to split the selected records into groups based on unique combinations of the group by columns. This then allows us to use aggregate functions (eg. MAX, MIN, SUM, AVG, ...) that will be applied to each group of records in turn. The database will return a single result record for each grouping.

For example, if we have a set of records representing temperatures over time and location in a table like this:

Location   Time    Temperature
--------   ----    -----------
London     12:00          10.0
Bristol    12:00          12.0
Glasgow    12:00           5.0
London     13:00          14.0
Bristol    13:00          13.0
Glasgow    13:00           7.0
...

Then if we want to find the maximum temperature by location, then we need to split the temperature records into groupings, where each record in a particular group has the same location. We then want to find the maximum temperature of each group. The query to do this would be as follows:

SELECT Location, MAX(Temperature)
FROM Temperatures
GROUP BY Location;
a'r
  • 35,921
  • 7
  • 66
  • 67
  • thanks that helps, if only you could further explain what `GROUP BY` is useful for, as without it, the query seems to return the same data – Frantisek Mar 18 '11 at 17:50
  • 1
    added some further explanation on group by – a'r Mar 18 '11 at 18:19
  • Very nice solution. Combing MAX() and GROUP BY produces a result I was not expecting but works perfect. For LEFT JOINs when tableB has multiple rows and you want a specific row joined in. Thanks! – factorypolaris Jul 29 '17 at 22:50
26

Several other answer give the solution using MAX. In some scenarios using an agregate function is either not possilbe, or not performant.

The alternative that I use a lot is to use a correlated sub-query in the join...

SELECT
   `userName`,
   `carPrice`
FROM `users`
LEFT JOIN `cars`
ON cars.id = (
  SELECT id FROM `cars` WHERE BelongsToUser = users.id ORDER BY carPrice DESC LIMIT 1
)
WHERE `id`='4'
MatBailie
  • 83,401
  • 18
  • 103
  • 137
  • What scenarios are those (to help people decide between subqueries and `group by`)? – Matt Fenwick Feb 08 '12 at 21:12
  • 6
    If the `cars` table contains other information like make, model, license plate, for example. This method finds the individual record and all it's fields. The MAX() approach only finds the value for one field. It depends on the real intent of the question (max price) vs (car with max price) – MatBailie Feb 08 '12 at 21:21
  • Neat. I think that it suits better the request, which is how to order a join by some value.. You're allowed to use other operators like limit. – funder7 Jan 12 '23 at 18:20
24

Older MySQL versions this is enough:

SELECT
    `userName`,
    `carPrice`
FROM `users`
LEFT JOIN (SELECT * FROM `cars` ORDER BY `carPrice`) as `cars`
ON cars.belongsToUser=users.id
WHERE `id`='4'

Nowdays, if you use MariaDB the subquery should be limited.

SELECT
    `userName`,
    `carPrice`
FROM `users`
LEFT JOIN (SELECT * FROM `cars` ORDER BY `carPrice` LIMIT 18446744073709551615) as `cars`
ON cars.belongsToUser=users.id
WHERE `id`='4'
Georgi Nikolov
  • 1,155
  • 1
  • 11
  • 15
  • 1
    At often usefull to add a small explanation to your answers – Blowsie Feb 19 '13 at 11:30
  • 7
    Always try to avoid sub-selects like this unless you have no other option. This may well work fine on a small dataset but start working with thousands or millions of rows and your will end up experiencing performance issues at best. – DangerPaws Oct 16 '15 at 12:46
8

This will get you the most expensive car for the user:

SELECT users.userName, MAX(cars.carPrice)
FROM users
LEFT JOIN cars ON cars.belongsToUser=users.id
WHERE users.id=4
GROUP BY users.userName

However, this statement makes me think that you want all of the cars prices sorted, descending:

So question: How do I set the LEFT JOIN table to be ordered by carPrice, DESC ?

So you could try this:

SELECT users.userName, cars.carPrice
FROM users
LEFT JOIN cars ON cars.belongsToUser=users.id
WHERE users.id=4
GROUP BY users.userName
ORDER BY users.userName ASC, cars.carPrice DESC
trautwein
  • 480
  • 1
  • 5
  • 14
Forgotten Semicolon
  • 13,909
  • 2
  • 51
  • 61
2

try this out:

   SELECT
      `userName`,
      `carPrice`
   FROM `users`
   LEFT JOIN `cars`
   ON cars.belongsToUser=users.id
   WHERE `id`='4'
   ORDER BY `carPrice` DESC
   LIMIT 1

Felix

Itay Moav -Malimovka
  • 52,579
  • 61
  • 190
  • 278
Felix Geenen
  • 2,465
  • 1
  • 28
  • 37
  • `ORDER BY carPrice DESC`, you mean? This will work for a single user, yes. But not multiple. – Dan J Mar 18 '11 at 17:25
  • @djacobson he is filtering by user id. So group by is a more generalized solution. On the other hand, this is a faster solution, if he really filters by user id all the time – Itay Moav -Malimovka Mar 18 '11 at 17:28