SQL Server-Index- / Leistungshilfe erforderlich (Index-Scan und Sortierung in 40 Minuten)


7

Ich habe endlich die Logik für eine Abfrage herausgefunden, jetzt muss ich sie beschleunigen ... dramatisch, wenn möglich (sie läuft 40 Minuten +/-). Es steht auf zwei Tischen, einer mit nur ein paar hundert Zeilen ( tblTradingDays) und der andere mit über einer Million ( tblDailySMA). Es werden 48 Millionen Zeilen zurückgegeben.

Meine grundlegende Logik lautet: Ich gebe für jede Kombination aus Symbol-, Handelsdatum- und Periodenwerten eine Zeile zurück und erstelle ein "langsames v schnelles" Periodenpaar. In der Originaltabelle gibt es also ein Symbol, ein Handelsdatum, einen Zeitraum (5,10,20, ...) und den tatsächlichen Wert für diese Kombination. Ich möchte die Werte für alle Kombinationen von Perioden vergleichen (außer Periode p1 = Periode p2). Ich nummeriere die Datensätze, die für die weitere Verarbeitung verwendet werden sollen. Hoffe das macht Sinn.

Ich habe verschiedene Indizes und Kombinationen der Felder ausprobiert, die an den Verknüpfungen und Prädikaten beteiligt sind. Im geschätzten Ausführungsplan sieht es so aus, als wäre eine Sortierung die teuerste Operation. Der einzige Index tblDailySMA, der im Plan angezeigt wird, obwohl ich andere hinzufüge, ist nicht gruppiert, eindeutig, auf: Symbol, TradeDate, Periodund enthält Value. Es wird gescannt. Die Frage ist unten, hoffentlich kann mir jemand helfen. Danke im Voraus..

SELECT 
   ROW_NUMBER() OVER 
   (
        ORDER BY t1.Symbol, t1.Period, t2.Period, t.TradingDate DESC
   ) RowNum,
   t1.Symbol, t.TradingDate, t1.Period, t2.Period, t1.Value FastValue, 
   t2.Value SlowValue, (t1.Value - t2.Value) SlowFastDiff,
   ChgSign = CASE WHEN t1.Value < t2.Value THEN 0 
                  WHEN t1.Value >= t2.Value THEN 1 
                  WHEN t1.Value IS NULL OR t2.Value IS NULL THEN NULL 
             END
FROM 
    tblTradingDays t 
RIGHT JOIN 
    tblDailySMA t1 ON t.TradingDate = t1.TradeDate
INNER JOIN 
    tblDailySMA t2 ON t1.Symbol = t2.Symbol AND t1.TradeDate  = t2.TradeDate
WHERE 
    t1.Period < t2.Period

Hier ist der Ausführungsplan:

  |--Compute Scalar(DEFINE:([Expr1007]=[Market].[dbo].[tblDailySMA].[Value] as [t1].[Value]-[Market].[dbo].[tblDailySMA].[Value] as [t2].[Value], [Expr1008]=CASE WHEN [Market].[dbo].[tblDailySMA].[Value] as [t1].[Value]<[Market].[dbo].[tblDailySMA].[Value] as [t2].[Value] THEN (0) ELSE CASE WHEN [Market].[dbo].[tblDailySMA].[Value] as [t1].[Value]>=[Market].[dbo].[tblDailySMA].[Value] as [t2].[Value] THEN (1) ELSE NULL END END))
       |--Sequence Project(DEFINE:([Expr1006]=row_number))
            |--Segment
                 |--Parallelism(Gather Streams, ORDER BY:([t1].[Symbol] ASC, [t1].[Period] ASC, [t2].[Period] ASC, [t].[TradingDate] DESC))
                      |--Sort(ORDER BY:([t1].[Symbol] ASC, [t1].[Period] ASC, [t2].[Period] ASC, [t].[TradingDate] DESC))
                           |--Hash Match(Right Outer Join, HASH:([t].[TradingDate])=([t1].[TradeDate]), RESIDUAL:([Market].[dbo].[tblTradingDays].[TradingDate] as [t].[TradingDate]=[Market].[dbo].[tblDailySMA].[TradeDate] as [t1].[TradeDate]))
                                |--Parallelism(Distribute Streams, Hash Partitioning, PARTITION COLUMNS:([t].[TradingDate]))
                                |    |--Index Scan(OBJECT:([Market].[dbo].[tblTradingDays].[PK_tblTradingDays] AS [t]))
                                |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([t1].[TradeDate]))
                                     |--Merge Join(Inner Join, MANY-TO-MANY MERGE:([t1].[Symbol], [t1].[TradeDate])=([t2].[Symbol], [t2].[TradeDate]), RESIDUAL:([Market].[dbo].[tblDailySMA].[Symbol] as [t1].[Symbol]=[Market].[dbo].[tblDailySMA].[Symbol] as [t2].[Symbol] AND [Market].[dbo].[tblDailySMA].[TradeDate] as [t1].[TradeDate]=[Market].[dbo].[tblDailySMA].[TradeDate] as [t2].[TradeDate] AND [Market].[dbo].[tblDailySMA].[Period] as [t1].[Period]<[Market].[dbo].[tblDailySMA].[Period] as [t2].[Period]))
                                          |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([t1].[Symbol], [t1].[TradeDate]), ORDER BY:([t1].[Symbol] ASC, [t1].[TradeDate] ASC))
                                          |    |--Index Scan(OBJECT:([Market].[dbo].[tblDailySMA].[IX_tblDailySMA_Noncl_SymbTrDatePer] AS [t1]), ORDERED FORWARD)
                                          |--Parallelism(Repartition Streams, Hash Partitioning, PARTITION COLUMNS:([t2].[Symbol], [t2].[TradeDate]), ORDER BY:([t2].[Symbol] ASC, [t2].[TradeDate] ASC))
                                               |--Index Scan(OBJECT:([Market].[dbo].[tblDailySMA].[IX_tblDailySMA_Noncl_SymbTrDatePer] AS [t2]), ORDERED FORWARD)

Können Sie Ihren Ausführungsplan veröffentlichen?
Mwigdahl

Sie können auch erklären, was Sie versuchen zu tun. Die Struktur, die Sie hier haben, ist eine dreieckige Verknüpfung, die mit großen Tabellen überhaupt nicht gut skaliert werden kann.
Mwigdahl

Sie beginnen mit der größten Tabelle und verbinden dann kleinere Tabellen damit.
Kermit

Wofür brauchst du tblTradingDays? Sieht so aus, als würde es nichts tun. Oder möchten Sie vielleicht LEFT JOINstattdessen RIGHT JOIN?

@YuxiuLi Guter Fang, ich habe gerade behoben, dass ... den Daten in tblDailySMA einige Werte fehlen, daher verwende ich einen linken äußeren Join, um ihn streng zu halten.

Antworten:


3

Wenn ich diese Art von Leistungsproblemen in einer großen Abfrage habe, teile ich sie in kleine Abfragen mit temporären Tabellen auf. Für mich ist es eine Lösung und das Leistungsverhältnis kann 10 zu 1 oder mehr betragen.

Erster Schritt:

with t1 as (
SELECT 
  t1.Symbol, 
  t1.Period, 
  t.TradingDate,
  t1.Value as FastValue
FROM      tblDailySMA t1 
LEFT JOIN tblTradingDays t 
        ON t.TradingDate = t1.TradeDate
) 
select * 
into #t1
from t1;

--I include period into idex to avoid table access on next query
create index t1_idx on ( Symbol, TradeDate, Period ) 

Zweiter Schritt:

with t2 as (
SELECT
  t1.Symbol, 
  t1.Period as period_t1, 
  t.TradingDate,
  t1.Value as FastValue
  t2.Period as period_t2,
  t2.Value as SlowValue,
  t2.TradeDate
FROM       #t1 as t1
INNER JOIN tblDailySMA t2 
   ON t1.Symbol = t2.Symbol AND t1.TradeDate  = t2.TradeDate
WHERE t1.Period < t2.Period
)
select 
  *
into #t2
from t2;

--Here create indexes for t2
--Here next and final query 

Und so weiter. Ein Vorteil dieses Systems besteht darin, dass Sie Abfragen Schritt für Schritt verbessern können.


Temp Tabelle kann leicht die Protokolldatei sehr schnell erhöhen?
Testen

@NilishGarg, danke für deinen Kommentar, aber OP spricht nicht über Speicherbeschränkungen. Warum machen Sie uns darauf aufmerksam?

Eigentlich spricht er von 48 Millionen Zeilen. Ich hatte vorher das gleiche Problem. Ich habe die gleiche Initiative ergriffen (temporäre Tische). Dies verursachte jedoch die schlimmste Situation. Warum? Die Festplatte belegte aufgrund der .ldf-Datei innerhalb einer Woche 22 GB Speicherplatz. In wissen, dass dies das Problem aber vorübergehend beheben wird.
Testen

1
Ich benutze oder verlasse mich nie auf die Protokolldatei. Ich mache jede Nacht eine vollständige Sicherung.
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.