Ich habe ein Tabellenschema ähnlich dem folgenden (vereinfacht):
CREATE TABLE Transactions
(
TransactionID int NOT NULL IDENTITY(1, 1) PRIMARY KEY CLUSTERED,
CustomerID int NOT NULL, -- Foreign key, not shown
TransactionDate datetime NOT NULL,
...
)
CREATE INDEX IX_Transactions_Customer_Date
ON Transactions (CustomerID, TransactionDate)
Zur Erläuterung: Diese Transaktionstabelle fasst verschiedene Arten von Transaktionen aus der Datenbank eines anderen Anbieters zusammen (wir nennen das einen ETL-Prozess), und ich habe daher keine große Kontrolle über die Reihenfolge, in der sie eingefügt werden. Selbst wenn ich sie hätte, könnten die Transaktionen zurückdatiert werden, daher ist es wichtig zu wissen, dass das Maximum TransactionID
für jede gegebene customer
ist nicht unbedingt die jüngste Transaktion.
Tatsächlich ist die jüngste Transaktion eine Kombination aus dem Datum et die ID. Die Daten sind nicht eindeutig - der Anbieter schneidet die Tageszeit oft ab. Um die jüngste Transaktion zu finden, muss ich also zunächst das jüngste Datum und dann die jüngste ID für dieses Datum ermitteln.
Ich weiß, dass ich dies mit einer Fensterabfrage tun kann ( ROW_NUMBER() OVER (PARTITION BY TransactionDate DESC, TransactionID DESC)
), aber dies erfordert einen vollständigen Index-Scan und eine sehr teure Sortierung, und scheitert daher kläglich in Bezug auf die Effizienz. Außerdem ist es ziemlich umständlich, die ganze Zeit zu schreiben.
Etwas effizienter ist die Verwendung von zwei CTEs oder verschachtelten Unterabfragen, eine zum Auffinden der MAX(TransactionDate)
per CustomerID
und eine weitere, um die MAX(TransactionID)
. Auch hier funktioniert es, erfordert aber ein zweites Aggregat und eine Verknüpfung, was etwas besser ist als die ROW_NUMBER()
Abfrage, aber immer noch ziemlich mühsam in Bezug auf die Leistung.
Ich habe auch in Betracht gezogen, ein CLR User-Defined Aggregate zu verwenden und werde darauf zurückgreifen, wenn nötig, aber ich würde es vorziehen, eine reine SQL-Lösung zu finden, wenn möglich, um die Bereitstellung zu vereinfachen (es gibt keine Notwendigkeit für SQL-CLR irgendwo sonst in diesem Projekt).
Die Frage lautet also konkret:
Ist es möglich, eine Abfrage zu schreiben, die die neueste TransactionID
per CustomerID
definiert als das Maximum TransactionID
für die letzte TransactionDate
und einen Plan zu erreichen, der in seiner Leistung einem gewöhnlichen MAX
/ GROUP BY
abfragen?
(Mit anderen Worten: Die einzigen wichtigen Schritte im Plan sollten ein Index-Scan und ein Stream-Aggregat sein. Mehrere Scans, Sortierungen, Joins usw. werden wahrscheinlich zu langsam sein).