How do I join the most recent row in one table to another table?

SqlDateJoinGreatest N-per-Group

Sql Problem Overview


I have data that looks like this:

entities
id         name
1          Apple
2          Orange
3          Banana

Periodically, a process will run and give a score to each entity. The process generates the data and adds it to a scores table like so:

scores 
id  entity_id    score   date_added
1    1            10       1/2/09
2    2            10       1/2/09
3    1            15       1/3/09
4    2            10       1/03/09
5    1            15       1/4/09
6    2            15       1/4/09
7    3            22       1/4/09

I want to be able to select all of the entities along with the most recent recorded score for each resulting in some data like this:

entities
id name     score  date_added
1  Apple     15     1/4/09
2  Orange    15     1/4/09
3  Banana    15     1/4/09

I can get the data for a single entity using this query:

SELECT entities.*, 
       scores.score, 
       scores.date_added 
FROM entities
     
INNER  JOIN scores
ON entities.id = scores.entity_id
     
WHERE entities.id = ?
     
ORDER BY scores.date_added DESC
LIMIT 1

But I'm at a loss for how to select the same for all entities. Perhaps it's staring me in the face?

Thank you very kindly for taking the time.

Thanks for the great responses. I'll give it a few days to see if a preferred solution bubbles up then I'll select the answer.

UPDATE: I've tried out several of the proposed solutions, the main issue I'm facing now is that if an entity does not yet have a generated score they don't appear in the list.

What would the SQL look like to ensure that all entities are returned, even if they don't have any score posted yet?

UPDATE: Answer selected. Thanks everyone!

Sql Solutions


Solution 1 - Sql

I do it this way:

SELECT e.*, s1.score, s1.date_added 
FROM entities e
  INNER JOIN scores s1
    ON (e.id = s1.entity_id)
  LEFT OUTER JOIN scores s2
    ON (e.id = s2.entity_id AND s1.id < s2.id)
WHERE s2.id IS NULL;

Solution 2 - Sql

Just to add my variation on it:

SELECT e.*, s1.score
FROM entities e
INNER JOIN score s1 ON e.id = s1.entity_id
WHERE NOT EXISTS (
	SELECT 1 FROM score s2 WHERE s2.id > s1.id
)

Solution 3 - Sql

approach 1

SELECT entities.*, 
       scores.score, 
       scores.date_added 
FROM entities

INNER  JOIN scores
ON entities.id = scores.entity_id

WHERE scores.date_added = 
  (SELECT max(date_added) FROM scores where entity_id = entities.id)

Solution 4 - Sql

I know this is a old question, just thought I'd add a approach no-one has mentioned yet, Cross Apply or Outer Apply. These are available in SQL Server 2005 (the database type is not tagged in this question) Or higher

Using the temporary tables

DECLARE @Entities TABLE(Id INT PRIMARY KEY, name NVARCHAR(MAX))
INSERT INTO @Entities
VALUES (1, 'Apple'), (2, 'Orange'), (3, 'Banana'), (4, 'Cherry')

DECLARE @Scores TABLE(Id INT PRIMARY KEY, Entity_Id INT, Score INT, Date_Added DATE)
INSERT INTO @Scores
VALUES (1,1,10,'2009-02-01'),
(2,2,10,'2009-02-01'),
(3,1,15,'2009-02-01'),
(4,2,10,'2009-03-01'),
(5,1,15,'2009-04-01'),
(6,2,15,'2009-04-01'),
(7,3,22,'2009-04-01')

You could use

SELECT E.Id, E.name, S.Score, S.Date_Added 
FROM @Entities E
CROSS APPLY
(
	SELECT TOP 1 * 
	FROM @Scores Sc 
	WHERE Sc.Entity_Id = E.Id  
	ORDER BY sc.Score DESC
) AS S

to get the desired results. The equivilent to allow entities without scores would be

SELECT E.Id, E.name, S.Score, S.Date_Added 
FROM @Entities E
OUTER APPLY
(
	SELECT TOP 1 * 
	FROM @Scores Sc 
	WHERE Sc.Entity_Id = E.Id  
	ORDER BY sc.Score DESC
) AS S

Solution 5 - Sql

approach 2

query cost relative to batch:


SELECT entities.*, 
       scores.score, 
       scores.date_added 
FROM entities

INNER  JOIN scores
ON entities.id = scores.entity_id

inner join 
    (
	SELECT 
	       entity_id, max(date_added) as recent_date
	FROM scores
	group by entity_id
	) as y on entities.id = y.entity_id and scores.date_added = y.recent_date

Solution 6 - Sql

SELECT entities.*, 
       scores.score, 
       scores.date_added 
FROM entities

INNER  JOIN scores
ON entities.id = scores.entity_id

WHERE entities.id in 
(select id from scores s2 where date_added = max(date_added) and s2.id = entities.id)

ORDER BY scores.date_added DESC
LIMIT 1

Solution 7 - Sql

You can also do this today in most RDBMSs (Oracle, PostgreSQL, SQL Server) with a natural query using window functions such as ROW_NUMBER:

SELECT id, name, score, date_added FROM (
 SELECT e.id, e.name, s.score, s.date_added,
 ROW_NUMBER() OVER (PARTITION BY e.id ORDER BY s.date_added DESC) rn
 FROM Entities e INNER JOIN Scores s ON e.id = s.entity_id
) tmp WHERE rn = 1;

SQL Fiddle

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGloryFishView Question on Stackoverflow
Solution 1 - SqlBill KarwinView Answer on Stackoverflow
Solution 2 - SqlRay HidayatView Answer on Stackoverflow
Solution 3 - SqlMichael BuenView Answer on Stackoverflow
Solution 4 - SqlManatherinView Answer on Stackoverflow
Solution 5 - SqlMichael BuenView Answer on Stackoverflow
Solution 6 - SqlOtávio DécioView Answer on Stackoverflow
Solution 7 - SqlCristian ScutaruView Answer on Stackoverflow