2009-07-16 10 views
60

Estoy tratando de obtener la ip, el usuario y la marca de tiempo más reciente de una tabla que puede contener tanto la IP actual para un usuario como una o más ips. Me gustaría una fila para cada usuario que contenga la IP más reciente y la marca de tiempo asociada. Así que si una tabla es el siguiente:Consulta SQL para obtener la fila más reciente para cada instancia de una clave determinada

username  | ip  | time_stamp 
--------------|----------|-------------- 
ted   | 1.2.3.4 | 10 
jerry   | 5.6.6.7 | 12 
ted   | 8.8.8.8 | 30 

que esperaría el resultado de la consulta a ser:

jerry | 5.6.6.7 | 12 
ted  | 8.8.8.8 | 30 

¿Puedo hacer esto en una sola consulta SQL? En caso de que importe, el DBMS es Postgresql.

Respuesta

90

Prueba esto:

Select u.[username] 
     ,u.[ip] 
     ,q.[time_stamp] 
From [users] As u 
Inner Join (
    Select [username] 
      ,max(time_stamp) as [time_stamp] 
    From [users] 
    Group By [username]) As [q] 
On u.username = q.username 
And u.time_stamp = q.time_stamp 
+0

Estaba en el camino correcto pero no pude conseguir la unión correcta. Esto hizo el truco. ¡Gracias! – alanc10n

+2

¿Funciona esto en SQL Server? Intenté exactamente lo mismo en datos similares, pero obtengo una fila por cada "ip" junto con la marca de tiempo más reciente. – tcash21

+0

Utilizado en una tabla HANA, esto produjo varias filas donde se esperaba 1. HANA tiene muchas cosas que no son lo mismo que la mayoría de los populares motores SQL. –

7

Algo como esto:

select * 
from User U1 
where time_stamp = (
    select max(time_stamp) 
    from User 
    where username = U1.username) 

debe hacerlo.

+0

Eso funcionaría si 'time_stamp' fuera único y no se puede asumir eso. – Nux

3

Ambas respuestas anteriores asumen que solo tiene una fila para cada usuario y marca de tiempo. Dependiendo de la aplicación y la granularidad de su time_stamp, esta puede no ser una suposición válida. Si necesita lidiar con lazos de time_stamp para un usuario determinado, deberá extender una de las respuestas dadas anteriormente.

Escribir esto en una consulta requeriría otra subconsulta anidada: las cosas se volverán más complicadas y el rendimiento puede verse afectado.

Me hubiera encantado haber agregado esto como un comentario, pero aún no tengo 50 reputación, ¡lo siento mucho por publicar una nueva respuesta!

+5

"Los dos anteriores ..." no es una buena manera de comenzar una respuesta. ¿Cómo puedo saber si las respuestas anteriores son a las que se refiere? Las respuestas pueden aparecer en un orden diferente según su puntaje. –

+6

Rob, no creo que le estés diciendo algo que él no sepa.NO PODÍA comentar y las respuestas a las que se refería eran obviamente defectuosas. ¿Qué es más importante, diseminar conocimiento o criticar dónde se colocó el texto? – cruskai239

0

He estado usando esto porque estoy devolviendo resultados de otra tabla. Aunque estoy tratando de evitar la unión anidada si ayuda con un paso menos. Oh bien. Devuelve lo mismo.

select 
users.userid 
, lastIP.IP 
, lastIP.maxdate 

from users 

inner join (
    select userid, IP, datetime 
    from IPAddresses 
    inner join (
     select userid, max(datetime) as maxdate 
     from IPAddresses 
     group by userid 
     ) maxIP on IPAddresses.datetime = maxIP.maxdate and IPAddresses.userid = maxIP.userid 
    ) as lastIP on users.userid = lastIP.userid 
+0

Creo que esta es la respuesta del usuario anterior – jawz101

29

solución elegante agradable con la función de ventana ROW_NUMBER (con el apoyo de PostgreSQL - ver en SQL Fiddle):

SELECT username, ip, time_stamp FROM (
SELECT username, ip, time_stamp, 
    ROW_NUMBER() OVER (PARTITION BY username ORDER BY time_stamp DESC) rn 
FROM Users 
) tmp WHERE rn = 1; 
+2

Esta es la mejor respuesta, ya que no implica una combinación en una consulta anidada. El único problema es que debe incluir la [ip] en la clave y el nombre de usuario según la pregunta. – dashnick

1

no pueden publicar comentarios por el momento, pero la respuesta de @Cristi S funciona un regalo para mí .

En mi situación, necesitaba mantener solo los 3 registros más recientes en Lowest_Offers para todos los product_ids.

Necesita volver a trabajar su SQL para eliminar - pensó que esto estaría bien, pero la sintaxis es incorrecta.

DELETE from (
SELECT product_id, id, date_checked, 
    ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY date_checked DESC) rn 
FROM lowest_offers 
) tmp WHERE > 3; 
+0

SQL Corregido: DELETE FROM Lowest_Offer donde id en ( \t ID SELECT FROM \t ( \t \t product_id SELECT, ID, date_checked, \t \t ROW_NUMBER() OVER (partición POR ORDEN product_id POR date_checked DESC) rn \t \t FROM Lowest_Offer \t) tmp DONDE rn> 3 ) – err1

Cuestiones relacionadas