SQL Server: WYBIERZ tylko wiersze z MAX (DATE)


109

Mam tabelę danych (db to 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

Chciałbym zadać zapytanie, które zwraca OrderNO, PartCode i Quantity, ale tylko dla ostatniego zarejestrowanego zamówienia.

Z tabeli przykładowej chciałbym odzyskać następujące informacje:

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

Zwróć uwagę, że tylko jeden wiersz został zwrócony dla zamówienia 9999.

Dzięki!


2
W swoim komentarzu przejdź do ROW_NUMBER (). Może wyglądać na dłuższą, ale z mojego doświadczenia wynika, że ​​jest o wiele szybsza przy odpowiednich indeksach.
MatBailie,

Dzięki Dems, doceniam twój wysiłek.
GEMI

1
@GEMI tylko z ciekawości, nie MAX(DATE)zwraca ani jednej linii dla zamówienia 9999?
Zameer Ansari,

Tak, ale chciałem, aby każde inne zamówienie zwracało tylko ostatnią linię zamówienia.
GEMI,

Odpowiedzi:


185

Jeśli rownumber() over(...)jest dostępny dla Ciebie ....

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
Dzięki Mikael Eriksson, to niesamowite zapytanie!
GEMI

57

Najlepszym sposobem jest Mikael Eriksson, jeśli ROW_NUMBER()jest dostępny.

Następną najlepszą rzeczą jest przyłączenie się do zapytania, zgodnie z odpowiedzią Cularis.

Alternatywnie, najprostszym i najprostszym sposobem jest skorelowane pod-zapytanie w klauzuli WHERE.

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

Lub...

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

30
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))

Jest to najszybsze ze wszystkich zapytań podanych powyżej. Koszt zapytania wyniósł 0,0070668.

Preferowana odpowiedź powyżej, autorstwa Mikaela Erikssona, ma koszt zapytania wynoszący 0,0146625

Możesz nie przejmować się wydajnością dla tak małej próbki, ale w dużych zapytaniach wszystko się sumuje.


2
Okazało się to dla mnie nieznacznie szybsze niż inne rozwiązania tutaj na zbiorze danych ~ 3,5 mln wierszy, jednak SSMS zasugerował indeks, który skrócił czas wykonania o połowę. Dzięki!
easuter

Szybko i prosto. Dzięki.
Stephen Zeng

Mam 100 tys. Wierszy i dla mnie zapytanie Mikaela Erikssona 3 razy szybciej. Może dlatego, że mam funkcję ROUND w klauzuli partycji po.
Wachburn

Jeśli masz pole daty o tej samej wartości (15.04.2017) dla 2 różnych identyfikatorów, zwróci ono 2 wiersze ...
Portekoi

Tak, Portekoi, to prawda, ale bez innego sposobu rozróżnienia tych dwóch rzędów, jak można wybrać jeden z nich? Możesz umieścić TOP na wyniku, ale skąd wiesz, że nie jest to drugi wiersz, który chcesz?
dźwięk

11
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)

Zapytanie wewnętrzne wybiera wszystkie OrderNoz ich maksymalną datą. Aby uzyskać inne kolumny tabeli, możesz dołączyć do nich OrderNoi MaxDate.


1

W przypadku MySql możesz wykonać następujące czynności:

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

Nie możesz wybrać ID w tabeli wewnętrznej, jeśli grupujesz według numeru zamówienia
Jacob

@Dems thanks @ cularis tak, to dotyczy MySql, w pytaniu nie sprecyzowano jaki silnik bazy danych
bencobb

Jeśli kod pocztowy, próbki XML lub danych, należy zaznaczyć te linie w edytorze tekstowym i kliknij na przycisk „Kod próbki” ( { }) na pasku narzędzi edytora, aby ładnie format i składnia go podświetlić!
marc_s

To jest MSSQL, przepraszam za to.
GEMI

1

Możesz również użyć tej instrukcji select jako zapytania z lewym złączeniem ... Przykład:

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

Mam nadzieję, że pomoże to komuś, kto tego szuka :)


1

rownumber () over (...) działa, ale nie podobało mi się to rozwiązanie z 2 powodów. - Ta funkcja nie jest dostępna, gdy używasz starszej wersji SQL, takiej jak SQL2000 - Zależność od funkcji i nie jest tak naprawdę czytelna.

Innym rozwiązaniem jest:

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

Jeśli masz indeksowany identyfikator i OrderNo, możesz użyć IN: (nienawidzę prostoty handlu ze względu na niejasność, tylko po to, aby zapisać kilka cykli):

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

0

Staraj się unikać IN używać 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
Dlaczego unikać IN? Czy masz jakieś argumenty na poparcie swojej opinii?
Preza8

wykonanie zapytania zajmie dużo czasu. Możesz przeczytać następujący artykuł xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik To jest artykuł z 2006 roku. Czy masz jakieś niedawne dowody tego, co mówisz?
Félix Gagnon-Grenier

0

To zadziałało dla mnie doskonale.

    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

To działa dla mnie. użyj MAX (CONVERT (data, ReportDate)), aby upewnić się, że masz wartość daty

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.