SQL Server: SELECT nur die Zeilen mit MAX (DATE)


109

Ich habe eine Datentabelle (die Datenbank ist 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

Ich möchte eine Abfrage durchführen, die OrderNO, PartCode und Quantity zurückgibt, jedoch nur für die zuletzt registrierte Bestellung.

Aus der Beispieltabelle möchte ich folgende Informationen zurückerhalten:

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

Beachten Sie, dass für die Bestellung 9999 nur eine Zeile zurückgegeben wurde.

Vielen Dank!


2
Gehen Sie aus Ihrem Kommentar mit der Antwort ROW_NUMBER (). Es mag länger aussehen, aber meiner Erfahrung nach ist es mit geeigneten Indizes am schnellsten.
MatBailie

Danke Dems, ich schätze deine Mühe.
GEMI

1
@GEMI nur aus Neugier, Gibt nicht MAX(DATE)eine Zeile für Bestellung 9999 zurück?
Zameer

Ja, aber ich wollte, dass jede andere Bestellung nur die letzte Bestellposition zurückgibt.
GEMI

Antworten:


184

Wenn rownumber() over(...)für Sie verfügbar ist ....

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
Danke Mikael Eriksson, das ist eine großartige Frage!
GEMI

56

Der beste Weg ist Mikael Eriksson, falls ROW_NUMBER()er Ihnen zur Verfügung steht.

Das nächstbeste ist die Teilnahme an einer Abfrage gemäß der Antwort von Cularis.

Alternativ ist der einfachste und direkteste Weg eine korrelierte Unterabfrage in der WHERE-Klausel.

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

Oder...

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

Dies ist die schnellste aller oben angegebenen Abfragen. Die Abfragekosten betrugen 0,0070668.

Die oben bevorzugte Antwort von Mikael Eriksson hat Abfragekosten von 0,0146625

Die Leistung eines so kleinen Beispiels interessiert Sie vielleicht nicht, aber bei großen Abfragen summiert sich alles.


2
Dies stellte sich für mich als geringfügig schneller heraus als die anderen Lösungen hier in einem ~ 3,5 Mio. Zeilendatensatz. SSMS schlug jedoch einen Index vor, der die Ausführungszeit halbierte. Vielen Dank!
Easuter

Schnell und unkompliziert. Vielen Dank.
Stephen Zeng

Ich habe 100.000 Zeilen und für mich Mikael Erikssons Abfrage dreimal schneller. Vielleicht liegt es daran, dass ich die ROUND-Funktion in der Partition by-Klausel habe.
Wachburn

Wenn Sie ein Datumsfeld mit dem gleichen Wert (15.04.2017) für 2 verschiedene IDs haben, werden 2 Zeilen zurückgegeben ...
Portekoi

Ja, Portekoi, das stimmt, aber ohne eine andere Möglichkeit, die beiden Zeilen zu unterscheiden, wie können Sie eine über die andere auswählen? Sie könnten dem Ergebnis ein TOP hinzufügen, aber woher wissen Sie, dass es nicht die andere Zeile ist, die Sie wollen?
Ton

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)

Die innere Abfrage wählt alle OrderNomit ihrem maximalen Datum aus. Um die anderen Spalten der Tabelle abzurufen, können Sie sie OrderNound die verbinden MaxDate.


1

Für MySql können Sie Folgendes tun:

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

Sie können keine ID in der inneren Tabelle auswählen, wenn Sie nach Bestellnummer gruppieren
Jacob

@Dems danke @ cularis ja, dies bezieht sich auf MySQL, die Frage hat nicht angegeben, welche Datenbank-Engine
bencobb

Wenn Sie eine Postleitzahl, XML oder Datenproben, bitte diese Zeilen im Texteditor und klicken Sie auf den „Code - Beispiele“ Taste (markieren { }) auf der Editor - Symbolleiste zu schön Format und die Syntax markieren Sie es!
marc_s

Dies ist MSSQL, sorry dafür.
GEMI

1

Und Sie können diese select-Anweisung auch als Abfrage für die linke Verknüpfung verwenden ... Beispiel:

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

Hoffe das hilft jemandem, der danach sucht :)


1

rownumber () over (...) funktioniert, aber diese Lösung hat mir aus zwei Gründen nicht gefallen. - Diese Funktion ist nicht verfügbar, wenn Sie eine ältere SQL-Version wie SQL2000 verwenden. - Funktionsabhängigkeit und nicht wirklich lesbar.

Eine andere Lösung ist:

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

Wenn Sie ID und OrderNo indiziert haben, können Sie IN: (Ich hasse es, Einfachheit gegen Dunkelheit zu tauschen, nur um einige Zyklen zu sparen):

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

0

Vermeiden Sie 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
Warum IN vermeiden? Haben Sie Argumente, die Ihre Meinung stützen?
Preza8

Die Ausführung Ihrer Abfrage dauert lange. Sie können folgenden Artikel lesen xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik Dies ist ein Artikel aus dem Jahr 2006. Haben Sie einen aktuellen Beweis dafür, was Sie sagen?
Félix Gagnon-Grenier

0

Das hat bei mir ganz gut geklappt.

    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

Das funktioniert bei mir. Verwenden Sie MAX (CONVERT (Datum, ReportDate)) , um sicherzustellen, dass Sie einen Datumswert haben

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.