Pregunta

Tengo datos que se ven así:

entities
id         name
1          Apple
2          Orange
3          Banana

Periódicamente, se ejecutará un proceso y otorgará una puntuación a cada entidad. El proceso genera los datos y los agrega a una tabla de puntajes de la siguiente manera:

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

Quiero poder seleccionar todas las entidades junto con el puntaje registrado más reciente para cada resultado de algunos datos como este:

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

Puedo obtener los datos para una sola entidad usando esta consulta:

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

Pero no sé cómo seleccionar lo mismo para todas las entidades. ¿Quizás me está mirando a la cara?

Muchas gracias por tomarse el tiempo.

Gracias por las excelentes respuestas. Le daré unos días para ver si surge una solución preferida y luego seleccionaré la respuesta.

ACTUALIZACIÓN: He probado varias de las soluciones propuestas, el problema principal que enfrento ahora es que si una entidad aún no tiene un puntaje generado, no aparecen en la lista.

¿Cómo se vería el SQL para garantizar que se devuelvan todas las entidades, incluso si aún no tienen puntaje publicado?

ACTUALIZACIÓN: Respuesta seleccionada. ¡Gracias a todos!

¿Fue útil?

Solución

Lo hago de esta manera:

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;

Otros consejos

Solo para agregar mi variación:

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
)

enfoque 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)

enfoque 2

costo de consulta relativo al lote:


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

Sé que esta es una vieja pregunta, solo pensé en agregar un enfoque que nadie ha mencionado todavía, Cross Apply o Outer Apply. Están disponibles en SQL Server 2005 (el tipo de base de datos no está etiquetado en esta pregunta) o superior

Uso de las tablas temporales

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')

Podrías usar

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

para obtener los resultados deseados. El privilegio para permitir entidades sin puntajes sería

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
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

También puede hacerlo hoy en la mayoría de los RDBMS (Oracle, PostgreSQL, SQL Server) con una consulta natural utilizando funciones de ventana como 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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top