Ich habe die folgende Abfrage und aufgrund vieler SUM
Funktionsaufrufe wird meine Abfrage zu langsam ausgeführt. Ich habe viele Datensätze in meiner Datenbank und möchte für jeden einen Bericht aus dem aktuellen und dem letzten Jahr (letzte 30 Tage, letzte 90 Tage und letzte 365 Tage) erhalten:
SELECT
b.id as [ID]
,d.[Title] as [Title]
,e.Class as [Class]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 30 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 30 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 90 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 90 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Current - Last 365 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Current - Last 365 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 30 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(MONTH,-13,GETDATE()) and a.DateCol <= DATEADD(MONTH,-12,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 30 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 90 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(QUARTER,-5,GETDATE()) and a.DateCol <= DATEADD(QUARTER,-4,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 90 Days Col2]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col1 ELSE 0 END) as [Last year - Last 365 Days Col1]
,Sum(CASE WHEN a.DateCol >= DATEADD(YEAR,-2,GETDATE()) and a.DateCol <= DATEADD(YEAR,-1,GETDATE()) THEN a.col2 ELSE 0 END) as [Last year - Last 365 Days Col2]
FROM
tb1 a
INNER JOIN
tb2 b on a.id=b.fid and a.col3 = b.col4
INNER JOIN
tb3 c on b.fid = c.col5
INNER JOIN
tb4 d on c.id = d.col6
INNER JOIN
tb5 e on c.col7 = e.id
GROUP BY
b.id, d.Title, e.Class
Hat jemand eine Idee, wie ich meine Abfrage verbessern kann, um schneller zu laufen?
BEARBEITEN: Ich wurde ermutigt, den DATEADD
Funktionsaufruf in die where
Anweisung zu verschieben und zuerst zwei Jahre zu laden und dann in Spalten zu filtern. Ich bin jedoch nicht sicher, ob die vorgeschlagene Antwort ausgeführt wird und funktioniert. Sie finden sie hier: https: // stackoverflow. com / a / 59944426/12536284
Wenn Sie mit der oben genannten Lösung einverstanden sind, zeigen Sie mir bitte, wie ich sie in meiner aktuellen Abfrage anwenden kann.
Nur zu Ihrer Information, ich verwende diesen SP in C #, Entity Framework (DB-First), ungefähr so:
var result = MyDBEntities.CalculatorSP();
Execution Plan
. BitteAntworten:
Wie bereits erwähnt, ist der Ausführungsplan in diesem Fall sehr hilfreich. Basierend auf dem, was Sie gezeigt haben, haben Sie anscheinend 12 Spalten mit insgesamt 15 Spalten extrahiert
tb1 (a)
, sodass Sie versuchen können, Ihre Abfrage ohne Verknüpfung und nur gegen die auszuführentb1
, um festzustellen, ob Ihre Abfrage wie erwartet funktioniert. Da ich nichts Falsches an Ihren SUM-Funktionsaufrufen sehen kann, ist meine beste Vermutung, dass Sie ein Problem mit Ihren Joins haben. Ich würde Folgendes vorschlagen. Sie können beginnen, indem Sie beispielsweise den letzten JoinINNER JOIN tb5 e on c.col7 = e.id
und die damit verbundene Verwendung wiee.Class as [Class]
und ausschließene.Class
in Ihrer Gruppe nach Aussage. Wir werden es nicht vollständig ausschließen. Dies ist nur ein Test, um sicherzustellen, ob das Problem damit besteht oder nicht. Wenn Ihre Abfrage besser ausgeführt wird und Sie wie erwartet versuchen können, eine temporäre Tabelle als Problemumgehung anstelle des letzten Joins zu verwenden , etwas wie das:Temporäre Tabellen sind Tabellen, die vorübergehend auf dem SQL Server vorhanden sind. Die temporären Tabellen sind nützlich, um die unmittelbaren Ergebnismengen zu speichern, auf die mehrmals zugegriffen wird. Weitere Informationen finden Sie hier https://www.sqlservertutorial.net/sql-server-basics/sql-server-temporary-tables/ Und hier https://codingsight.com/introduction-to-temporary-tables-in -SQL Server/
Außerdem würde ich dringend empfehlen, wenn Sie die gespeicherte Prozedur verwenden, setzen Sie diese Option
NOCOUNT
aufON
, sie kann auch eine erhebliche Leistungssteigerung bewirken, da der Netzwerkverkehr stark reduziert wird:Basierend darauf :
quelle
tb5
auf den#Temp
Tisch und Verbinden der temporären Tabelle schneller arbeiten als zum Verbindentb5
direkt? Sicherlich enthalten sie dieselben Daten (und#Temp
möglicherweise fehlt ein Index, wenn er in vorhanden wartb5
). Ich kann wirklich nicht verstehen, warum dies effizienter ist (soweit ich weiß, sollte es weniger effizient sein, alle Daten zu kopieren und beizutreten).tb5
auf einem anderen Server befindet? In diesem Fall ist die Verwendung einer temporären Tabelle definitiv schneller als die direkte Verknüpfung mit einem anderen Server. Das war nur ein Vorschlag, um zu testen und zu sehen, ob sich etwas geändert hat. Ich hatte in der Vergangenheit eine ähnliche Situation, und es scheint, dass die temporäre Tabelle dem OP zum Glück auch in diesem Fall geholfen hat.Der beste Ansatz ist das Einfügen in eine Tabellenvariable / Hash-Tabelle (wenn die Zeilenanzahl klein ist, verwenden Sie eine Tabellenvariable oder eine Hash-Tabelle, wenn die Zeilenanzahl ziemlich groß ist). Aktualisieren Sie dann die Aggregation und wählen Sie schließlich aus der Tabellenvariablen oder Hash-Tabelle aus. Ein Blick in den Abfrageplan ist erforderlich.
quelle
Ich gehe davon aus, dass tb1 eine große Tabelle ist (relativ zu tb2, tb3, tb4 und tb5).
In diesem Fall ist es hier sinnvoll, die Auswahl dieser Tabelle einzuschränken (mit einer WHERE-Klausel).
Wenn nur ein kleiner Teil von tb1 verwendet wird, z. B. weil die Verknüpfungen mit tb2, tb3, tb4 und tb5 die erforderlichen Zeilen auf nur wenige Prozent reduzieren, sollten Sie überprüfen, ob die Tabellen in den Spalten indiziert sind, die Sie in den Verknüpfungen verwenden .
Wenn ein großer Teil von tb1 verwendet wird, kann es sinnvoll sein, die Ergebnisse zu gruppieren, bevor sie mit tb2, tb3, tb4 und tb5 verknüpft werden. Unten ist ein Beispiel dafür.
quelle
Verwenden Sie einfach berechnete Spalten
Beispiel
Geben Sie berechnete Spalten in einer Tabelle an
quelle
Um solche Berechnungen zu optimieren, sollten Sie einige der Werte vorberechnen. Die Idee von Vorberechnungen besteht darin, die Anzahl der Zeilen zu reduzieren, die gelesen oder fortgesetzt werden müssen.
Eine Möglichkeit, dies zu erreichen, ist die Verwendung einer indizierten Ansicht und die Engine die Berechnungen selbst durchführen zu lassen. Da diese Art von Ansichten einige Einschränkungen aufweist, erstellen Sie am Ende eine einfache Tabelle und führen stattdessen die Berechnungen durch. Grundsätzlich hängt es von den geschäftlichen Anforderungen ab.
Im folgenden Beispiel erstelle ich eine Tabelle mit
RowID
undRowDatetime
Spalten und füge 1 Million Zeilen ein. Ich verwende eine indizierte Ansicht, um die Entitäten pro Tag zu zählen. Anstatt 1 Million Zeilen pro Jahr abzufragen, frage ich 365 Zeilen pro Jahr ab, um diese Metriken zu zählen.Der Erfolg einer solchen Lösung hängt stark davon ab, wie die Daten verteilt sind und wie viele Zeilen Sie haben. Wenn Sie beispielsweise für jeden Tag des Jahres einen Eintrag pro Tag haben, stimmen Ansicht und Tabelle mit den Zeilen überein, sodass die E / A-Vorgänge nicht reduziert werden.
Das Obige ist nur ein Beispiel für das Materialisieren und Lesen der Daten. In Ihrem Fall müssen Sie möglicherweise weitere Spalten zur Ansichtsdefinition hinzufügen.
quelle
Ich würde eine Nachschlagetabelle "Dates" verwenden, um meine Daten mit einem Index für DatesId zu verknüpfen. Ich verwende die Daten als Filter, wenn ich historische Daten durchsuchen möchte. Der Join ist schnell und daher die Filterung, da die DatesId ein Clustered-Primärindex (Primärschlüssel) ist. Fügen Sie auch die Datumsspalte (als eingeschlossene Spalte) für Ihre Datentabelle hinzu.
Die Datumstabelle enthält die folgenden Spalten:
DatesId, Date, Year, Quarter, YearQuarter, MonthNum, MonthNameShort, YearWeek, WeekNum, DayOfYear, DayOfMonth, DayNumOfWeek, DayName
Beispieldaten: 20310409 2031-04-09 2031 2 2031-Q2 4. April Apr 2031_15 15 99 9 3 Mittwoch
Sie können mir eine PM senden, wenn Sie eine CSV davon möchten, damit Sie sie in die Datenbank importieren können, aber ich bin sicher, dass Sie so etwas online leicht finden und Ihre eigene erstellen können.
Ich füge auch eine Identitätsspalte hinzu, damit Sie für jedes Datum eine Ganzzahl erhalten können. Dies erleichtert die Arbeit etwas, ist jedoch keine Voraussetzung.
Dadurch kann ich leicht zu einer bestimmten Zeit zurückspringen. Es ist ganz einfach, eigene Ansichten dazu zu erstellen. Sie können die Funktion ROW_NUMBER () natürlich auch für Jahre, Wochen usw. verwenden.
Sobald ich den gewünschten Datenbereich habe, verbinde ich mich mit den Daten. Funktioniert sehr schnell!
quelle
Da Sie Werte immer basierend auf einer ganzen Anzahl von Monaten gruppieren, würde ich zuerst in einer Unterabfrage in der from-Klausel nach Monat gruppieren. Dies ähnelt der Verwendung einer temporären Tabelle. Nicht sicher, ob dies Ihre Anfrage tatsächlich beschleunigen würde.
quelle
Um die Geschwindigkeit der SQL-Abfrage zu verbessern, müssen Sie Indizes hinzufügen. Für jede verknüpfte Tabelle müssen Sie einen Index hinzufügen.
Wie dieses Codebeispiel für Orakel:
quelle