SQL Server: SELECCIONE solo las filas con MAX (DATE)


109

Tengo una tabla de datos (la base de datos es MSSQL):

    ID  OrderNO PartCode  Quantity DateEntered
    417 2144     44917    100      18-08-11
    418 7235     11762    5        18-08-11
    419 9999     60657    100      18-08-11
    420 9999     60657    90       19-08-11

Me gustaría hacer una consulta que devuelva OrderNO, PartCode y Quantity, pero solo para el último pedido registrado.

De la tabla de ejemplo, me gustaría recuperar la siguiente información:

     OrderNO PartCode  Quantity     
     2144     44917    100      
     7235     11762    5        
     9999     60657    90      

Tenga en cuenta que solo se devolvió una línea para el pedido 9999.

¡Gracias!


2
De su comentario, vaya con la respuesta ROW_NUMBER (). Puede parecer más largo, pero, en mi experiencia, es mucho más rápido con los índices adecuados.
MatBailie

Gracias demócratas, agradezco su esfuerzo.
GEMI

1
@GEMI solo por curiosidad, ¿no MAX(DATE)devuelve una línea para el pedido 9999?
Zameer

Sí, pero quería que cada pedido diferente devolviera solo la última línea de pedido.
GEMI

Respuestas:


184

Si rownumber() over(...)está disponible para ti ...

select OrderNO,
       PartCode,
       Quantity
from (select OrderNO,
             PartCode,
             Quantity,
             row_number() over(partition by OrderNO order by DateEntered desc) as rn
      from YourTable) as T
where rn = 1      

2
Gracias Mikael Eriksson, ¡esta es una consulta increíble!
GEMI

56

La mejor forma es Mikael Eriksson, si ROW_NUMBER()está disponible para usted.

La siguiente mejor opción es unirse a una consulta, según la respuesta de Cularis.

Alternativamente, la forma más simple y directa es una subconsulta correlacionada en la cláusula WHERE.

SELECT
  *
FROM
  yourTable AS [data]
WHERE
  DateEntered = (SELECT MAX(DateEntered) FROM yourTable WHERE orderNo = [data].orderNo)

O...

WHERE
  ID = (SELECT TOP 1 ID FROM yourTable WHERE orderNo = [data].orderNo ORDER BY DateEntered DESC)

29
select OrderNo,PartCode,Quantity
from dbo.Test t1
WHERE EXISTS(SELECT 1
         FROM dbo.Test t2
         WHERE t2.OrderNo = t1.OrderNo
           AND t2.PartCode = t1.PartCode
         GROUP BY t2.OrderNo,
                  t2.PartCode
         HAVING t1.DateEntered = MAX(t2.DateEntered))

Esta es la más rápida de todas las consultas proporcionadas anteriormente. El costo de la consulta fue de 0.0070668.

La respuesta preferida anterior, de Mikael Eriksson, tiene un costo de consulta de 0.0146625

Es posible que no le importe el rendimiento de una muestra tan pequeña, pero en consultas grandes, todo suma.


2
Esto me resultó marginalmente más rápido que las otras soluciones aquí en un conjunto de datos de filas de ~ 3.5M, sin embargo, SSMS sugirió un índice que redujo el tiempo de ejecución a la mitad. ¡Gracias!
este

Rápido y sencillo. Gracias.
Stephen Zeng

Tengo 100k filas y para mí la consulta de Mikael Eriksson 3 veces más rápida. Tal vez sea porque tengo la función ROUND en partición por cláusula.
Wachburn

Si tiene un campo de fecha con el mismo valor (15/04/2017) para 2 ID diferentes, devolverá 2 filas ...
Portekoi

Sí Portekoi, eso es cierto, pero sin otra forma de diferenciar las dos filas, ¿cómo se puede seleccionar una sobre la otra? Podría poner un TOP en el resultado, pero ¿cómo sabe que no es la otra fila que desea?
tono

10
SELECT t1.OrderNo, t1.PartCode, t1.Quantity
FROM table AS t1
INNER JOIN (SELECT OrderNo, MAX(DateEntered) AS MaxDate
            FROM table
            GROUP BY OrderNo) AS t2
ON (t1.OrderNo = t2.OrderNo AND t1.DateEntered = t2.MaxDate)

La consulta interna selecciona todos OrderNocon su fecha máxima. Para obtener las otras columnas de la tabla, puede unirlas OrderNoy el MaxDate.


1

Para MySql, puede hacer algo como lo siguiente:

select OrderNO, PartCode, Quantity from table a
join (select ID, MAX(DateEntered) from table group by OrderNO) b on a.ID = b.ID

No puede seleccionar ID en la tabla interna si agrupa por número de pedido
Jacob

@Dems gracias @ cularis sí, esto se refiere a MySql, la pregunta no especificó qué motor de base de datos
bencobb

Si el código de poste, XML o datos de muestras, por favor, destacar aquellas líneas en el editor de texto y haga clic en el botón "muestras de código" ( { }) en la barra de herramientas de editor de formato y la sintaxis muy bien resáltala!
marc_s

Esto es MSSQL, lo siento.
GEMI

1

Y también puede usar esa declaración de selección como consulta de unión izquierda ... Ejemplo:

... left join (select OrderNO,
   PartCode,
   Quantity from (select OrderNO,
         PartCode,
         Quantity,
         row_number() over(partition by OrderNO order by DateEntered desc) as rn
  from YourTable) as T where rn = 1 ) RESULT on ....

Espero que esto ayude a alguien que busque esto :)


1

rownumber () over (...) está funcionando pero no me gustó esta solución por 2 razones. - Esta función no está disponible cuando utiliza una versión anterior de SQL como SQL2000 - Depende de la función y no es realmente legible.

Otra solucion es:

SELECT tmpall.[OrderNO] ,
       tmpall.[PartCode] ,
       tmpall.[Quantity] ,
FROM   (SELECT [OrderNO],
               [PartCode],
               [Quantity],
               [DateEntered]
        FROM   you_table) AS tmpall
       INNER JOIN (SELECT [OrderNO],
                          Max([DateEntered]) AS _max_date
                   FROM   your_table
                   GROUP  BY OrderNO ) AS tmplast
               ON tmpall.[OrderNO] = tmplast.[OrderNO]
                  AND tmpall.[DateEntered] = tmplast._max_date

1

Si tiene ID indexado y OrderNo, puede usar IN: (Odio cambiar la simplicidad por oscuridad, solo para guardar algunos ciclos):

select * from myTab where ID in(select max(ID) from myTab group by OrderNo);

0

Trate de evitar EN uso JOIN

SELECT SQL_CALC_FOUND_ROWS *  FROM (SELECT  msisdn, callid, Change_color, play_file_name, date_played FROM insert_log
   WHERE play_file_name NOT IN('Prompt1','Conclusion_Prompt_1','silent')
 ORDER BY callid ASC) t1 JOIN (SELECT MAX(date_played) AS date_played FROM insert_log GROUP BY callid) t2 ON t1.date_played=t2.date_played

1
¿Por qué evitar IN? ¿Tiene algún argumento para apoyar su opinión?
Preza8

llevará mucho tiempo ejecutar su consulta. Puede leer el siguiente artículo xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik Este es un artículo de 2006. ¿Tiene alguna prueba reciente de lo que está diciendo?
Félix Gagnon-Grenier

0

Esto funcionó para mí perfectamente bien.

    select name, orderno from (
         select name, orderno, row_number() over(partition by 
           orderno order by created_date desc) as rn from orders
    ) O where rn =1;

-1

Esto funciona para mi. use MAX (CONVERT (date, ReportDate)) para asegurarse de tener el valor de la fecha

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.