SQL Server: SELEZIONA solo le righe con MAX (DATE)


109

Ho una tabella di dati (il db è 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

Vorrei fare una query che restituisca OrderNO, PartCode e Quantity, ma solo per l'ultimo ordine registrato.

Dalla tabella di esempio vorrei recuperare le seguenti informazioni:

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

Si noti che è stata restituita solo una riga per l'ordine 9999.

Grazie!


2
Dal tuo commento, vai con la risposta ROW_NUMBER (). Potrebbe sembrare più lungo, ma, secondo la mia esperienza, è di gran lunga il più veloce con indici appropriati.
MatBailie

Grazie Dems, apprezzo il tuo impegno.
GEMI

1
@GEMI solo per curiosità, non MAX(DATE)restituisce una riga per l'ordine 9999?
Zameer

Sì, ma volevo che ogni ordine diverso restituisse solo l'ultima riga dell'ordine.
GEMI

Risposte:


184

Se rownumber() over(...)è disponibile per te ....

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
Grazie Mikael Eriksson, questa è una domanda fantastica!
GEMI

56

Il modo migliore è Mikael Eriksson, se ROW_NUMBER()è a tua disposizione.

La cosa migliore è partecipare a una query, come da risposta di Cularis.

In alternativa, il modo più semplice e diretto è una sottoquery correlata nella clausola 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))

Questa è la più veloce di tutte le query fornite sopra. Il costo della query è stato di 0,0070668.

La risposta preferita sopra, di Mikael Eriksson, ha un costo della query di 0,0146625

Potresti non interessarti delle prestazioni per un campione così piccolo, ma nelle query di grandi dimensioni, tutto si somma.


2
Questo mi è risultato leggermente più veloce delle altre soluzioni qui su un set di dati di righe di ~ 3,5 milioni, tuttavia SSMS ha suggerito un indice che dimezza i tempi di esecuzione. Grazie!
easuter

Veloce e semplice. Grazie.
Stephen Zeng

Ho 100k righe e per me la query di Mikael Eriksson è 3 volte più veloce. Forse è perché ho la funzione ROUND nella clausola di partizione per.
Wachburn

Se hai un campo data con lo stesso valore (04/15/2017) per 2 ID diversi, restituirà 2 righe ...
Portekoi

Sì Portekoi, è vero, ma senza nessun altro modo per differenziare le due righe, come puoi selezionarne una rispetto all'altra? Potresti mettere un TOP sul risultato, ma come fai a sapere che non è l'altra riga che vuoi?
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 query interna seleziona tutti OrderNocon la data massima. Per ottenere le altre colonne della tabella, puoi unirle su OrderNoe su MaxDate.


1

Per MySql puoi fare qualcosa come il seguente:

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

Non è possibile selezionare l'ID nella tabella interna se si raggruppa per numero ordine
Jacob

@Dems grazie @ cularis sì, questo si riferisce a MySql, la domanda non specificava quale motore di database
bencobb

Se si codice postale, campioni di XML o di dati, si prega di evidenziare quelle linee in editor di testo e fare clic sul pulsante "codice di campioni" ( { }) sulla barra degli strumenti Editor per ben formato e la sintassi evidenziarlo!
marc_s

Questo è MSSQL, mi dispiace per quello.
GEMI

1

E puoi anche usare quell'istruzione select come query di join sinistro ... Esempio:

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

Spero che questo aiuti qualcuno che cerca questo :)


1

rownumber () over (...) funziona ma non mi è piaciuta questa soluzione per 2 motivi. - Questa funzione non è disponibile quando si utilizza una versione precedente di SQL come SQL2000 - Dipendenza dalla funzione e non è realmente leggibile.

Un'altra soluzione è:

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 hai ID indicizzato e OrderNo puoi usare IN: (odio la semplicità del trading per oscurità, solo per salvare alcuni cicli):

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

0

Cerca di evitare 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
Perché evitare IN? Hai degli argomenti a sostegno della tua opinione?
Preza8

ci vorrà molto tempo per eseguire la query. Puoi leggere il seguente articolo xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik Questo è un articolo del 2006. Hai qualche prova recente di quello che stai dicendo?
Félix Gagnon-Grenier

0

Questo ha funzionato perfettamente per me.

    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

Questo funziona per me. usa MAX (CONVERT (date, ReportDate)) per assicurarti di avere il valore della data

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.