I know this is a common question, because I’ve asked it before, and have looked at several more, but still, despite basing my query on this, I cannot get it to work. see: Mysql join based on max(timestamp) and mysql LEFT join for right table max value
Basically I have a tables of different items (‘chairs’, ‘tables’) and each type has record with their own id for specific chairs or tables. Then I have a location table which keeps all the locations that have ever been, including the current one. The current location is noted by the MAX timestamp.
My query is meant to get all the items, and join to the current location. I have followed what others have said works, but it isn’t working for me. Any help is appreciated.
EDIT: I didn’t say how it fails: It gets records and makes the join, and it returns the max timestamp, but not the record info that corresponds with the MAX(timestamp) record. So it gives the right timestamp, but the floor and etc are NOT from the record with the MAX timestamp.
Thanks!
The query:
$q = "SELECT
'chairs' AS work_type,
chairs.id AS work_id,
chairs.title,
chairs.dimensions,
CurrentLocations.floor,
CurrentLocations.bin,
CurrentLocations.bay,
CurrentLocations.other
FROM chairs
LEFT JOIN (
SELECT
locations.id AS loc_id,
locations.work_type AS clwt,
locations.work_id AS clwi,
locations.floor,
locations.bin,
locations.bay,
locations.other,
MAX(locations.timestamp) AS latestLocation
FROM
locations
GROUP BY
locations.work_type, locations.work_id
) CurrentLocations
ON CurrentLocations.clwi = chairs.id AND CurrentLocations.clwt = 'chairs'
WHERE cur_loc LIKE '%19th Street%'
ORDER BY
FIELD(floor, 'Basement', '3rd Floor', '4th Floor', '5th Floor'),
bin,
bay,
other";
Try this:
Description:
You want:
So, for every item (that is a
chair) you want only the latestlocation. For everywork_idin tablelocation, you want theMAX(timestamp). This can be easily found with aGROUP BY, as you already found out:But you also want to use other columns from that table and if you simply add them in the select list, it shows wrong results. (Note that in most DBMS, in order to use columns that are not in the
GROUP BYlist, you’d have to apply an aggregation function, like you appliedMAX()totimestamp. MySQL allows this behaviour which if properly used, can have some benefits. But it hasn’t implemented it 100% failproof and thus applying it in some cases, like this one, can give wrong results. Postgres has a much better implementation of this feature that it won’t allow erroneous results.)So, what you have to do is use the above
GROUP BYquery as a derived table (you name itclorCurrentLocation, whatever you want) and join it to thelocationstable, using all columns from theGROUP BYlist and the aggregated one (timestamp):After that, you can join them to the
chairstable as usual. Since, you had achairs LEFT JOIN locationsin the original query, I kept these in the answer, too.Another minor modification was that since you had
CurrentLocations.work_id = 'chairs', you wanted only those rows from thelocationstable. So, it’s rather redundant to do first the grouping and then reject all rows that don’t match that condition. It’s probably more efficient to first apply the condition and then group by. The final grouping query and the join conditions are modified accordingly.Also note that an index on
(work_type, work_id, timestamp)will improve efficiency of the query (theclsubquery will be processed using only that index and theJOIN locationswill use the index, too, to locate the rows needed from the locations table.)