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.