Ich habe einen Tisch mit Transaktionen für einen Bus (Boarding Riders). Angesichts der Routen-ID und des Datums muss ich in einer anderen Tabelle nachschlagen, welchen Servicetyp es an diesem Tag ausgeführt hat. Die Busfahrpläne ändern sich höchstens alle 6 Monate, wobei die meisten Jahre unverändert bleiben.
Derzeit ist die Zeitplantabelle wie folgt definiert:
CREATE TABLE [dbo].[Routes](
[ID] [int] NOT NULL,
[RouteID] [int] NOT NULL,
[Type] [varchar](50) NOT NULL,
[StartDate] [datetime] NOT NULL,
PRIMARY KEY CLUSTERED
(
[ID] ASC
));
Ein Beispiel könnte folgendermaßen aussehen:
ID RouteID Type StartDate
-- ------- ------------ ----------
1 301 Standard 2015-01-01
2 301 Discontinued 2016-06-01
3 302 Standard 2015-01-01
4 302 ParaTrans 2017-01-01
Wenn ich also eine Transaktion vom 20.04.2015 für RouteID
301 habe , möchte ich "Standard" zurückerhalten. Wenn die Transaktion jedoch vom 20.01.2018 stammt , sollte sie "Eingestellt" zurückgeben. Bei Transaktionen vor dem 01.01.2015 sollte NULL (oder "" oder etwas anderes als ein Ergebnis zurückgegeben werden, das möglicherweise mit einer gültigen Antwort in Konflikt steht, z. B. "Standard", "Paratrans" oder "Discontinued").
Grundsätzlich stellt die Tabelle , dass Route 301 war eine Standardroute zwischen 2015.01.01 und 2016.05.31 (und damit alle Transaktionen während dieser Zeit sollte als „Standard“ eingestuft werden), dann wurde es aufgegebene auf 2016-06- 01 (über die aktuellen Tag, implizit , da es keine spätere Planänderung ist angegeben), während 302 eine Standardroute war 2015.01.01 durch 2016.12.31 , dann ein ParaTrans (it) Route nach.
Route Type Start End
----- ---- ----- ---
301
Standard 2015-01-01 2016-05-31
Discontinued 2016-06-01 Present
302
Standard 2015-01-01 2016-12-31
ParaTrans 2017-01-01 Present
Derzeit sieht die Abfrage dazu folgendermaßen aus:
SELECT
TRANSIT_DAY,
ROUTE_ID,
(SELECT TOP (1) Type FROM Routes
WHERE (RouteID = dbo.DAILY_SALES_DETAIL.ROUTE_ID)
AND (StartDate <= dbo.DAILY_SALES_DETAIL.TRANSIT_DAY)
ORDER BY StartDate DESC) AS NCTD_MODE
FROM dbo.DAILY_SALES_DETAIL
Fragen
Was ich wissen möchte, ist: Ist dies die effektivste Kombination aus (a) Struktur der Routes
Tabelle und (b) Abfrage, um dieses Ergebnis zu erzielen? Mit anderen Worten, könnte eine effizientere Abfrage mit der vorhandenen Struktur verwendet werden? Könnte eine Änderung der Routentabelle eine effizientere Abfrage ermöglichen?
Überlegungen
Die Transaktionstabelle wird täglich von einem Lieferanten importiert. Daher ist das Ändern des Schemas dieser Tabelle nicht trivial und wird bevorzugt vermieden. Noch wichtiger ist, dass diese Suche in einer Reihe von Tabellen und Datenbanken unter Verwendung von Transaktionen oder anderen busbezogenen Daten verwendet wird, die von mehreren Anbietern bezogen wurden. Dies ist nur ein einziges Beispiel. Wir haben einen Anbieter (und damit eine Datenbank) für Geldtransaktionen, einen anderen für die Anzahl der Fahrer und einen weiteren für die Leistung usw., wobei die Routennummer und das Datum die einzige zuverlässig konsistente Kennung für alle sind.
Die Routentabelle hat einen Index von (RouteID, StartDate)
. Derzeit befinden sich 56 Zeilen in der Routentabelle und 26 Millionen Zeilen in der Transaktionstabelle. Die Routentabelle besteht aus 45 Routen. Derzeit gibt es keine Routen mit mehr als 2 Zeilen oder einer Änderung. Es gibt keine Begrenzung für die Anzahl der Änderungen, die eine einzelne Route haben könnte, aber ich füge diese Statistik hinzu, um zu zeigen, dass die Anzahl auf absehbare Zeit wahrscheinlich gering bleiben wird.
Ich kann alle erforderlichen Indizes hinzufügen, um eine vorgeschlagene Abfrage zu optimieren. Bei der Frage geht es mehr darum, die beste Strategie zu finden, vorausgesetzt, alle angemessenen Optimierungen werden an den betrachteten Strategien vorgenommen, als darum, die beste Optimierung einer bestimmten Strategie zu finden.
db <> hier fummeln
Antworten:
Sie können die Leistung Ihres Setups wie in Ihrer Frage gezeigt steigern, indem Sie die
dbo.Routes
Tabelle in Folgendes ändern :Der Schlüssel hier ist , dass wir den Clustered - Index sind zu definieren, die ist die Tabelle, auf die Verbindung von
RouteID
undStartDate DESC
. Dadurch werden die Daten genau so bereitgestellt, wie es für die von Ihnen geschriebene Abfrage am effizientesten ist. Der Vorbehalt hier ist das Einfügen in diedbo.Routes
für eine vorhandene Route mit einem neuen Datum. Dies führt zu Seitenaufteilungen, da die Zeilen in absteigender Reihenfolge des Datums gefüllt werden. Bei einer kleinen Anzahl von Zeilen in der Routentabelle und der gelegentlichen Indexpflege sollte dies jedoch kein großes Problem sein.Stattdessen würde ich in Betracht ziehen, die
dbo.Routes
Tabelle so zu ändern, dass sie eineEndDate
Spalte enthält. Dadurch entfällt die Notwendigkeit, eine Unterabfrage mitTOP(1)
und durchzuführenORDER BY ...
. Etwas wie:Beachten Sie, dass der Clustered-Index jetzt aktiviert ist
(RouteID, StartDate ASC)
.Die Abfrage kann jetzt
INNER JOIN
anstelle der korrelierten Unterabfrage eine verwenden und sieht folgendermaßen aus:Auf diese Weise kann SQL Server einen einfachen Join für die innere Schleife ausführen, um Ergebnisse zu erhalten. Zugegeben, wenn Sie eine große Anzahl von Zeilen zurückgeben, ist eine erhebliche Sortierung erforderlich, die wahrscheinlich auf tempdb übergeht.
Mit dem unten gezeigten MCVE können wir Pläne für die beiden Varianten vergleichen. Der erste Plan ist Ihre ursprüngliche Abfrage mit der korrelierten Unterabfrage. Der zweite Plan enthält die
EndDate
Spalte.Die 2. Variante hat Plankosten, die etwa viermal niedriger sind als die 1. Variante. Die Sortieroperatoren in beiden Plänen fordern 108 MB Speicher an und verschütten über 9.000 Seiten an Tempdb. Es ist jedoch ziemlich unwahrscheinlich, dass Sie die gesamte Ergebnismenge anfordern, anstatt eine einzelne Route oder möglicherweise einen Datumsbereich zu erhalten. Wenn Sie einen Filter für eine einzelne Route hinzufügen, gibt es keine große Speicherzuweisung oder Verschüttung für Tempdb.
Was folgt, ist ein Beispiel- MCVE mit 10.000 Routenzeilen und 1.000.000 Transaktionszeilen, mit denen Tests für verschiedene Designs ausgeführt werden können:
Tun Sie dies in tempdb, um "Unfälle" mit echten Tabellen zu vermeiden.
Löschen Sie die Tabellen, falls vorhanden (dies funktioniert unter SQL Server 2016+):
Erstellen Sie die
dbo.Routes
Tabelle mit einem Clustered-Index fürRouteID, StartDate DESC
:Fügen Sie 10.000 Routenzeilen ein:
Erstellen Sie das
dbo.Transactions
mit einem Clustered-IndexROUTE_ID, TRANSIT_DAY
. Durch das Erstellen eines solchen Clustered-Index werden Abfragen optimiert, die sowohl nach Route als auch nach Tag gefiltert werden.Fügen Sie 1.000.000 Zeilen in die
dbo.Transactions
Tabelle ein:Für eine
Routes
Tabelle mit einerEndDate
Spalte, die für Vergleichstests verwendet werden kann, habe ich Folgendes verwendet:Fragen Sie beide Tabellen nach einer bestimmten Route ab:
Der Plan für die obige Abfrage:
E / A- und Zeitstatistik:
Abfrage für alle Transaktionen / Routen:
Der Plan:
Böse Verschüttung an Tempdb für Sortieroperator:
Wenn wir den Clustered-Index so ändern
dbo.Transactions
, dass er ist(TRANSIT_DAY, ROUTE_ID)
, und die vollständige Abfrage erneut ausführen, sehen wir einen Plan ohne diese hässliche Sortierung und Spill-to-Tempdb:quelle