it-swarm.com.de

Verbessern Sie die Abfrageleistung, wenn Sie fast alle Zeilen mit vielen "Gruppieren nach" -Spalten auswählen

Ich habe eine Tabelle mit 20 Spalten und ungefähr 600.000 Datensätzen. Die maximale Zeilengröße beträgt nur ca. 100 Byte. Die Tabelle wird alle paar Tage neu gefüllt, aber die Anzahl der Datensätze bleibt ungefähr gleich.

Derzeit gibt es nur einen einzigen Clustered-Index: eine int-Identitätsspalte für den Primärschlüssel.

Ich habe mehrere Abfragen und Ansichten, die sich auf diese Tabelle stützen. Die Ausführung dauert normalerweise 5-10 Sekunden. Wenn ich einfach alle Datensätze auswähle (select * from myTable) dauert es ungefähr 4 Sekunden, um alle Ergebnisse abzurufen.

Ich konnte keine relevanten Benchmarks für die Auswahl von 500.000 Datensätzen in SQL Server finden. Ist diese Zeit typisch?

Hier ist eine typische Abfrage, die ich für die Tabelle durchführe:

select  CO.Company
    ,CO.Location
    ,CO.Account
    ,CO.SalesRoute
    ,CO.Employee
    ,CO.ProductType
    ,CO.Item
    ,CO.LoadJDate
    ,CO.CommissionRate
    ,SUM(CO.[Extended Sales Price]) AS Sales_Dollars
    ,SUM(CO.[Delivered Qty]) AS Quantity
from    dbo.Commissions_Output CO
where   CO.[Extended Sales Price] <> 0
group by    CO.Company
        ,CO.Location
        ,CO.Account
        ,CO.SalesRoute
        ,CO.Employee
        ,CO.ProductType
        ,CO.Item
        ,CO.LoadJDate
        ,CO.CommissionRate

Wenn ich mindestens einen nicht gruppierten Index in der Tabelle habe, erhalte ich das folgende Ergebnis:

Scananzahl 18, logische Lesevorgänge 18372; CPU-Zeit = 24818 ms, verstrichene Zeit = 8614 ms.

Ich habe verschiedene Indizes und Kombinationen ausprobiert (Index für die Filterspalte, einschließlich der Gruppenspalten; Index für alle Filter-/Gruppenspalten und einschließlich der Aggregatspalten; usw.). Alle bieten die gleiche Leistung und verwenden fast immer den gleichen Ausführungsplan.

Wenn ich alle außer dem Clustered Index (PK) entferne, wird die Leistung häufig um bis zu 3-4 Sekunden verbessert. Die logischen Lesevorgänge werden reduziert, während die Scananzahl halbiert wird.

Einige Anmerkungen zu den Daten: Die Ergebnisse der select- und where-Klausel vor der Gruppierung betragen ungefähr 500.000 Zeilen (fast die gesamte Tabelle). Nur ungefähr 10.000 Zeilen werden durch Gruppierung kombiniert, wodurch nach dem Gruppieren immer noch ungefähr 500.000 Gesamtdatensätze übrig bleiben.

Der Ausführungsplan ohne nicht gruppierten Index zeigt, dass die teuersten Vorgänge eine Hash-Übereinstimmung (49%) und ein Clustered-Index-Scan (35%) für die where-Klausel sind. MSSMS empfiehlt, einen nicht gruppierten Index für [Extended Sales Price]. Der Ausführungsplan mit mindestens einem nicht gruppierten Index zeigt, dass die Sortierung (in den Spalten nach Gruppierung) die teuerste Operation ist.

Angesichts der Tatsache, dass diese Abfrage fast alle Datensätze zurückgibt und das Gruppieren die Anzahl der Zeilen kaum verringert, ist dies so schnell wie die Abfrage? Es scheint so langsam zu sein, und ich lese Artikel und SO Fragen zu Personen, die Hunderttausende von Zeilen in weniger als 1000 ms zurückgeben. Vermisse ich etwas oder ist dies eine ziemlich typische Geschwindigkeit? Das Normalisieren dieser Tabelle ist derzeit keine Option, und ich bin mir nicht sicher, wie viel das würde helfen.

Ein letzter Hinweis: Ich habe mehrere Ansichten und andere Abfragen, die das Verknüpfen mit dieser Tabelle beinhalten (es gibt einige Normalisierungen). Zuerst dachte ich, dass diese Ansichten und Abfragen wegen schlechter Verknüpfungen und dergleichen langsam waren, aber es sieht so aus, als ob der wahre Schuldige diese Tabelle und die ersten Abfragen darauf sind. Die meisten Abfragen und Ansichten funktionieren mit fast allen Daten in der Tabelle. Wenn ich eine einzelne Spalte oder einen kleinen Bruchteil von Zeilen auswähle, ist die Ausführungszeit in Ordnung, aber dies ist selten.

pdate : Hier sind alle Ausführungszeiten, Pläne und IO Statistiken. Ich habe nicht jede Abfrage hunderte Male ausgeführt, aber die Ausführungszeiten schienen nicht variieren um mehr als 1000 ms 'heiß' gegen 'kalt'.

Kein nicht gruppierter Index, keine MAXDOP-Einstellung : nonc_nomaxdop

Tabelle 'Commissions_Output'. Scananzahl 9, logische Lesevorgänge 11263, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Vorlesevorgänge 0.

CPU-Zeit = 6690 ms, verstrichene Zeit = 4605 ms. (maximale CPU-Zeit = 7516 ms, minimale verstrichene Zeit = 3754 ms.)

Bei nicht gruppiertem Index keine MAXDOP-Einstellung : nc_nomaxdop

Tabelle 'Commissions_Output'. Scananzahl 16, logische Lesevorgänge 6227

CPU-Zeit = 6591 ms, verstrichene Zeit = 3717 ms.

Kein nicht gruppierter Index, MAXDOP 1 : nonc_maxdop

Tabelle 'Commissions_Output'. Scananzahl 1, logische Lesevorgänge 10278

CPU-Zeit = 2656 ms, verstrichene Zeit = 4991 ms.

Bei nicht gruppiertem Index MAXDOP 1 : nc_maxdop

Tabelle 'Commissions_Output'. Scananzahl 1, logische Lesevorgänge 10278

CPU-Zeit = 2656 ms, verstrichene Zeit = 4991 ms.

Nicht gruppierter Index verwendet:

create nonclustered index IX_NC_Comm_Output on dbo.Commissions_Output([Extended Sales Price])
include (company, location, account, salesroute, employee, producttype, item, loadjdate, commissionrate, [delivered qty])
5
Zairja

Der von Ihnen getestete nicht gruppierte Index ist für diese Abfrage nicht der beste. Es kann für die WHERE -Klausel und für einen Index-Scan anstelle eines vollständigen Tabellenscans verwendet werden, kann jedoch nicht für GROUP BY Verwendet werden.

Der bestmögliche Index müsste ein Teilindex sein (um die unerwünschten Zeilen aus der Klausel WHERE zu filtern), dann alle in GROUP BY Verwendeten Spalten und dann INCLUDE alle anderen in SELECT verwendeten Spalten:

CREATE INDEX special_ix 
  ON dbo.Commissions_Output
    ( company, location, account, 
      salesroute, employee, producttype, 
      item, loadjdate, commissionrate ) 
INCLUDE 
  ( [Extended Sales Price], [Delivered Qty] ) 
WHERE 
  ( [Extended Sales Price] <> 0 ) ;
3
ypercubeᵀᴹ

Ich möchte das Problem aus einem anderen Blickwinkel betrachten.

Ich stimme @ypercube zu, dass Sie jederzeit einen Index erstellen können, um die Abfragen zu vereinfachen. Das gesagt:

  • sie haben erwähnt, dass die Tabelle relativ wenig Daten enthält
  • tisch wird nur einmal alle paar Tage neu aufgebaut
  • Sie haben gezeigt, dass die Aggregation über die Textspalten der teuerste Teil Ihrer typischen Abfrage ist, den Sie auch nach dem Erstellen eines Deckungsindex erleben.

Warum nicht weiter gehen und die Aggregationen im Voraus erstellen, damit die Abfragen nicht mehrmals bearbeitet werden müssen? Scheint ein idealer Fall für eine indizierte Ansicht zu sein, in der Sie die aggregierte Abfrageausgabe frühzeitig materialisieren würden, oder für eine herkömmliche dedizierte Tabelle, die Sie beim Laden von Daten in Commissions_Output Füllen würden. In beiden Fällen opfern Sie nur wenig Speicherplatz für eine deutlich verbesserte Leistung.

Indizierte Ansichten haben eine Reihe von Einschränkungen in Bezug auf die Umgebung, in der Sie sie verwenden möchten, haben jedoch den großen Vorteil, dass sie automatisch anstelle der ursprünglichen Tabelle verwendet werden nter bestimmten Umständen .

5
bartover