SQL Server: SELECIONE apenas as linhas com MAX (DATE)


109

Eu tenho uma tabela de dados (o banco de dados é 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

Gostaria de fazer uma consulta que retorne OrderNO, PartCode e Quantity, mas apenas para o último pedido registrado.

Da tabela de exemplo, gostaria de obter as seguintes informações:

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

Observe que apenas uma linha foi retornada para o pedido 9999.

Obrigado!


2
De seu comentário, vá com a resposta ROW_NUMBER (). Pode parecer mais longo, mas é, na minha experiência, o mais rápido com índices apropriados.
MatBailie

Obrigado Dems, agradeço seu esforço.
GEMI

1
@GEMI só por curiosidade, não MAX(DATE)retorna uma linha para o pedido 9999?
Zameer

Sim, mas eu queria que cada pedido diferente retornasse apenas a última linha de pedido.
GEMI de

Respostas:


184

Se rownumber() over(...)estiver disponível para você ....

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
Obrigado Mikael Eriksson, esta é uma pergunta incrível!
GEMI de

56

A melhor forma é Mikael Eriksson, se ROW_NUMBER()estiver disponível para você.

O próximo melhor é participar de uma consulta, conforme a resposta de Cularis.

Alternativamente, a maneira mais simples e direta é uma subconsulta correlacionada na cláusula WHERE.

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

Ou...

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 é a mais rápida de todas as consultas fornecidas acima. O custo da consulta foi 0,0070668.

A resposta preferida acima, de Mikael Eriksson, tem um custo de consulta de 0,0146625

Você pode não se preocupar com o desempenho de uma amostra tão pequena, mas em consultas grandes, tudo se soma.


2
Isso acabou para mim marginalmente mais rápido do que as outras soluções aqui em um conjunto de dados de ~ 3,5 milhões de linhas, no entanto, o SSMS sugeriu um índice que reduziu o tempo de execução pela metade. Obrigado!
easuter de

Rápido e direto. Obrigado.
Stephen Zeng

Tenho 100 mil linhas e para mim a consulta do Mikael Eriksson é 3 vezes mais rápida. Talvez seja porque eu tenho a função ROUND partição por cláusula.
Wachburn

Se você tiver um campo de data com o mesmo valor (15/04/2017) para 2 IDs diferentes, ele retornará 2 linhas ...
Portekoi

Sim Portekoi, isso é verdade, mas sem nenhuma outra maneira de diferenciar as duas linhas, como você pode selecionar uma em detrimento da outra? Você poderia colocar TOP no resultado, mas como saber se não é a outra linha que você deseja?
tom

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)

A consulta interna seleciona todos OrderNocom sua data máxima. Para obter as outras colunas da tabela, você pode juntá-las no OrderNoe no MaxDate.


1

Para o MySql, você pode fazer algo como o seguinte:

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

Você não pode selecionar o ID na tabela interna se agrupar pelo número do pedido
Jacob

@Dems thanks @ cularis sim, isso está se referindo ao MySql, a questão não especificou qual mecanismo de banco de dados
bencobb

Se você código postal, amostras de XML ou de dados, por favor destacar as linhas no editor de texto e clique no botão "código de amostras" ( { }) na barra de ferramentas do editor de bem formato e sintaxe destacá-lo!
marc_s

Este é o MSSQL, desculpe por isso.
GEMI

1

E você também pode usar essa instrução select como consulta de junção à esquerda ... Exemplo:

... 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 ajude alguém que procura por isso :)


1

rownumber () over (...) está funcionando, mas não gostei desta solução por 2 motivos. - Esta função não está disponível quando você usa uma versão anterior do SQL como SQL2000 - Dependência da função e não é realmente legível.

Outra solução é:

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

Se você indexou ID e OrderNo, você pode usar IN: (Eu odeio trocar simplicidade por obscuridade, apenas para salvar alguns ciclos):

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

0

Tente evitar IN use 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 que evitar IN? Você tem algum argumento para apoiar sua opinião?
Preza8 de

levará muito tempo para executar sua consulta. Você pode ler o seguinte artigo xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik Este é um artigo de 2006. Você tem alguma prova recente do que está dizendo?
Félix Gagnon-Grenier

0

Isso funcionou perfeitamente para mim.

    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

Isso funciona para mim. use MAX (CONVERT (date, ReportDate)) para se certificar de que tem o valor de data

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.