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!

GEMI
fuente
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      
Mikael Eriksson
fuente
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)
MatBailie
fuente
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.

tono
fuente
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.

Jacob
fuente
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
Bencobb
fuente
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 :)

idzi
fuente
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
Navid Golforoushan
fuente
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);
MortenB
fuente
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
rey neo
fuente
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;
Shubhankar Sarkar
fuente
-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]
usuario2662006
fuente