11

Tengo las siguientes tablas:

tblPerson:Recuperando los registros más recientes dentro de una consulta

PersonID | Name 
--------------------- 
    1  | John Smith 
    2  | Jane Doe 
    3  | David Hoshi 

tblLocation:

LocationID | Timestamp | PersonID | X | Y | Z | More Columns... 
--------------------------------------------------------------- 
    40  | Jan. 1st |  3 | 0 | 0 | 0 | More Info... 
    41  | Jan. 2nd |  1 | 1 | 1 | 0 | More Info... 
    42  | Jan. 2nd |  3 | 2 | 2 | 2 | More Info... 
    43  | Jan. 3rd |  3 | 4 | 4 | 4 | More Info... 
    44  | Jan. 5th |  2 | 0 | 0 | 0 | More Info... 

puedo producir una consulta SQL que obtiene los registros de ubicación de cada persona como tal:

SELECT LocationID, Timestamp, Name, X, Y, Z 
FROM tblLocation 
JOIN tblPerson 
ON tblLocation.PersonID = tblPerson.PersonID; 

para producir el siguiente:

LocationID | Timestamp | Name  | X | Y | Z | 
-------------------------------------------------- 
    40  | Jan. 1st | David Hoshi | 0 | 0 | 0 | 
    41  | Jan. 2nd | John Smith | 1 | 1 | 0 | 
    42  | Jan. 2nd | David Hoshi | 2 | 2 | 2 | 
    43  | Jan. 3rd | David Hoshi | 4 | 4 | 4 | 
    44  | Jan. 5th | Jane Doe | 0 | 0 | 0 | 

Mi problema es que sólo estamos preocupados por el más reciente registro de ubicación. Como tal, sólo estamos realmente interesados ​​en las filas siguientes: LocationID 41, 43 y 44.

La pregunta es: ¿Cómo podemos consultar estas tablas para darnos los datos más recientes sobre una persona por- ¿base? ¿Qué agrupación especial debe suceder para producir el resultado deseado?

+0

puede publicar la solución final plzz –

Respuesta

19

MySQL no tiene rango funcionalidad/análisis/ventanas.

SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z 
    FROM tblPerson tp 
    JOIN tblLocation tl ON tl.personid = tp.personid 
    JOIN (SELECT t.personid, 
       MAX(t.timestamp) AS max_date 
      FROM tblLocation t 
     GROUP BY t.personid) x ON x.personid = tl.personid 
          AND x.max_date = tl.timestamp 

SQL Server 2005 + y Oracle 9i + soporte de análisis, de modo que puede usar:

SELECT x.locationid, x.timestamp, x.name, x.X, x.Y, x.Z 
    FROM (SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z, 
       ROW_NUMBER() OVER (PARTITION BY tp.name ORDER BY tl.timestamp DESC) AS rank 
      FROM tblPerson tp 
      JOIN tblLocation tl ON tl.personid = tp.personid) x 
WHERE x.rank = 1 

Usando una variable para obtener igual que la funcionalidad ROW_NUMBER en MySQL:

SELECT x.locationid, x.timestamp, x.name, x.X, x.Y, x.Z 
    FROM (SELECT tl.locationid, tl.timestamp, tp.name, X, Y, Z, 
       CASE 
       WHEN @name != t.name THEN 
        @rownum := 1 
       ELSE @rownum := @rownum + 1 
       END AS rank, 
       @name := tp.name 
      FROM tblLocation tl 
      JOIN tblPerson tp ON tp.personid = tl.personid 
      JOIN (SELECT @rownum := NULL, @name := '') r 
     ORDER BY tp.name, tl.timestamp DESC) x 
WHERE x.rank = 1 
+0

Gracias OMG. Tomé SQL Server 2005 y lo cambié un poco para producir los resultados deseados. –

3

Esta es una pregunta clásica de "máximo por grupo" que aparece en Stack Overflow casi todos los días. Hay muchas formas de resolverlo y puede encontrar soluciones de ejemplo en searching Stack Overflow. Aquí es una manera de que se puede hacer en MySQL:

SELECT 
    location.LocationId, 
    location.Timestamp, 
    person.Name, 
    location.X, 
    location.Y, 
    location.Z 
FROM (
    SELECT 
     LocationID, 
     @rn := CASE WHEN @prev_PersonID = PersonID 
        THEN @rn + 1 
        ELSE 1 
       END AS rn, 
     @prev_PersonID := PersonID 
    FROM (SELECT @prev_PersonID := NULL) vars, tblLocation 
    ORDER BY PersonID, Timestamp DESC 
) T1 
JOIN tblLocation location ON location.LocationID = T1.LocationId 
JOIN tblPerson person ON person.PersonID = location.PersonID 
WHERE rn = 1 
3

Como @ Mark Byers menciona que este problema aparece frecuentemente en Stack Overflow.

Aquí está la solución que más frecuentemente recomiendo, teniendo en cuenta las tablas:

SELECT p.*, l1.* 
FROM tblPerson p 
JOIN tblLocation l1 ON p.PersonID = l1.PersonID 
LEFT OUTER JOIN tblLocation l2 ON p.PersonID = l2.PersonID AND 
    (l1.timestamp < l2.timestamp OR l1.timestamp = l2.timestamp AND l1.LocationId < l2.LocationId) 
WHERE l2.LocationID IS NULL; 

Para ver otros ejemplos, siga la etiqueta greatest-n-per-group, que he añadido a su pregunta.

+0

¡Gracias por agregar esa etiqueta para mí, Bill! –

Cuestiones relacionadas