n Datensätze pro Gruppierung anzeigen

Zugegeben ist das Subjekt nicht sehr treffend, aber im Moment fällt mir kein Besseres ein.

Gestern stellte jemand auf SQL Server Central.com die Frage, wie man zu jedem Namen mehr als ein Datum anzeigen kann. Genauer gesagt, die beiden aktuellsten Daten. Diese Frage kann man leicht auf die Northwind Beispieldatenbank übertragen: Zeige mir zu jedem Kunden die beiden letzten Bestelldaten ein.

Ein Weg, dies zu erreichen, ist folgender:

SELECT t1.CustomerID, t1.OrderDate
  FROM Orders t1
 WHERE t1.OrderDate IN
  (SELECT TOP 2 t2.OrderDate
     FROM Orders t2
    WHERE t2.CustomerID = t1.CustomerID
    ORDER BY t2.OrderDate DESC)
 ORDER BY t1.CustomerID, t1.OrderDate DESC

CustomerID OrderDate                                              
---------- ------------------------------------------------------ 
ALFKI      1998-04-09 00:00:00.000
ALFKI      1998-03-16 00:00:00.000
ANATR      1998-03-04 00:00:00.000
ANATR      1997-11-28 00:00:00.000
...

Sollte der Kunde mehr als einmal an einem dieser beiden Tage geordert haben, wird dies - auch ohne Angabe von "with ties" - berücksichtigt:

...
SAVEA      1998-05-01 00:00:00.000
SAVEA      1998-04-17 00:00:00.000
SAVEA      1998-04-17 00:00:00.000
...

Eine weitere Alternative bietet folgendes Statement:

SELECT t1.CustomerID , t1.OrderDate
  FROM Orders t1 
 WHERE 
  (SELECT COUNT(*) 
     FROM Orders 
    WHERE OrderDate<=t1.OrderDate AND CustomerID = t1.CustomerID) <=2 
 ORDER BY t1.CustomerID,  t1.OrderDate DESC

Zumindest in der Northwind Datenbank zeigt sich ein interessanter Unterschied wenn man den Output von SET STATISTICS IO betrachtet:

Table 'Orders'. Scan count 827, logical reads 269412, physical reads 0, read-ahead reads 0.

für das erste Statement und

Table 'Worktable'. Scan count 823, logical reads 3351, physical reads 0, read-ahead reads 0.
Table 'Orders'. Scan count 2, logical reads 44, physical reads 0, read-ahead reads 0.

für das zweite. Auch die Ausführungszeiten sprechen für sich

------------------------------------------------------ 
1900-01-01 00:00:01.243

(1 row(s) affected)

für Alternative 1, sowie

------------------------------------------------------ 
1900-01-01 00:00:00.080

(1 row(s) affected)

für Alternative 2. Also, zumindest in der Northwind Datenbank performt das zweite Statement deutlich besser als das erste.

Ein weiterer Unterschied zwischen beiden Statements soll nicht unerwähnt bleiben. Während das erste Statement "automatisch" Ties handhabt, und damit u.U. mehr als 2 Datensätze pro Gruppe wiedergibt, gibt das zweite Statement stets nur die "ersten beiden" Datensätze wieder, egal, wieviele weitere ebenfalls in die Gruppe passen würden.