Fragensteller
SQL Count Abfrage beschleunigen

Allgemeine Diskussion
-
Hallo,
ich speichere Messdaten in einer SQL Tabelle ab und überprüfe anschließen mit "select count(*) from myTable where Messungsnummer like 'xyz' " ob 7500 Datensätze zur Messung abgespeichert wurden.
Ist die Tabelle noch ziemlich klein, dauert die ganze Überprüfung ca. 70 ms. Im Moment sind ca 2500 Messungen a 7500 Datensätzen darin gespeichert. Jetzt dauert die Abfrage ca 8 s, was zu lange ist. Ich habe mir die Dauer der Überprüfungsvorgang bei jedem Speichervorgang in einer Textdatei ausgeben lassen und je voller die Tabelle ist, desto länger dauert die Überprüfung. Es werden dieses Jahr noch ca. 10000 Messungen dazukommen.
Wie kann ich sie beschleunigen? Ziel wären so 1-2 Sekunden Dauer. Ich habe einen Index auf die Spalte "Messungsnummer" gelegt.
Danke schon mal im voraus.
VG
Matthias
- Typ geändert Aleksander Chalabashiev Montag, 8. Februar 2016 08:53 keine bestätige Lösung
Alle Antworten
-
Hallo Matthias,
Wie ist denn das Zeitverhalten wenn du
Select Count(Messungsnummer) from mytable where Messungsnummer like 'xyZ'
Zudem ist ein like als Vergleichsoperator nicht gerade sehr schnell.
Gruß Benjamin
Benjamin Hoch
MCSE: Data Platform,
MCSA: Windows Server 2012, -
select count(*) from myTable where Messungsnummer like 'xyz'
Hallo Matthias,
wieso verwendest Du hier ein LIKE und kein =? Ist im Suchstring ein Wildcard enthalten? Wie sieht der Ausführungsplan der Abfrage aus? Wird während der Abfrage weiter Daten in die Tabelle geschrieben, was die Abfrage blockieren könnte?
Olaf Helper
[ Blog] [ Xing] [ MVP] -
Hallo
hier könnte man sich mal Clustered Columnstore Indexes ansehen. (SQL 2014)
https://msdn.microsoft.com/de-de/library/gg492153.aspx
Ansonsten müsste man sich das Schema und die darauf ablaufenden Prozesse ansehen, um zu sehen, was man da optimieren kann.
Andreas Wolter (Blog | Twitter)
MCSM: Microsoft Certified Solutions Master Data Platform, MCM, MVP
www.SarpedonQualityLab.com | www.SQL-Server-Master-Class.com- Bearbeitet Andreas.WolterMicrosoft employee Donnerstag, 28. Januar 2016 11:30 Link
-
Auch ein
SET STATISTICS IO ON; SET STATISTICS TIME ON;
vor der Abfrage kann Dir etwas mehr über IO und Zeiten verraten.
Kann es u. a. sein, dass die Statistik des Index in dem Moment des Selects veraltet ist und die Messungsnummer fortlaufend vergeben wurde? Die letzte ist also die höchste Nummer.
Dann könnte es sein, dass der Optimizer aus dem Index erschließt, dass es wahrscheinlich 0 Sätze zu der Messungsnummer gibt und geht direkt zu einem schlechteren Weg.Einen schönen Tag noch,
Christoph
--
Data Platform MVP - http://www.insidesql.org/blogs/cmu -
Ja, man kann sich den Ausführungsplan in SSMS Query Window anzeigen lassen: Anzeigen eines tatsächlichen Ausführungsplans
Nur man braucht schon etwas Erfahrung, um den lesen zu können. Du kannst ein Screenshot posten oder Du kannst ihn uns auch als Datei bereit stellen: Im Plan Rechte Maus => Speichern als, da kommt dann eine .sqlplan Datei, was aber nur XML ist; die könntest Du uns z.B. auf Deinem OneDrive bereit stellen.
Olaf Helper
[ Blog] [ Xing] [ MVP]- Bearbeitet Olaf HelperMVP Freitag, 29. Januar 2016 10:42
-
Hallo Matthias,
Dein Problem ist, dass Du keinen geeigneten Index für eine SEEK-Operation hast. Dein LIKE ist nicht problematisch, da Du keine Platzhalter verwendest. Der "Nachteil", wie von Benjamin Hoch angedeutet, ist hier zu vernachlässigen, da beide Varianten einen performanten INDEX SEEK verwenden. Das gilt aber nur so lange, wie die Verteilung der Daten möglichst gleichmässig ist (würde zu Deinem Problem aber deutlich zu weit führen).
Andreas hat Dich schon auf den Weg von Columnstore gewiesen; Nachteil dieser Lösung ist, dass es ein "ENTERPRISE" Feature ist und erst ab SQL 2014 updateable ist. Das halte ich für einen wichtigen Punkt, da Du ja schreibst, dass Du noch Daten eintragen willst
Die Lösung Deines Problems liegt in der richtigen Indexierung. Wenn Du ein COUNT(*) anwendest, sucht Microsoft SQL Server IMMER nach dem kleinsten Index. Der Query Optimizer ist cost based und berechnet die Optimierung unter anderem aus dem zu generierenden IO und den Kosten für die einzusetzenden Operatoren.
Für ein besseres Verständnis des Vorgehens habe ich eine Testtabelle mit insgesamt 50 Messungen á 7500 Datensätze erstellt. Diese Tabelle hat einen - für das Beispiel - trivialen Aufbau:
/* Tabelle mit Clustered Index erstellen */ CREATE TABLE dbo.Messungen ( Id int NOT NULL IDENTITY (1, 1), MessNr char(5) NOT NULL, MessWert int, Col1 char(300) NOT NULL DEFAULT ('Nur ein Lückenfüller') ); GO CREATE UNIQUE CLUSTERED INDEX cix_Messungen_Id ON dbo.Messungen (Id) WITH DROP_EXISTING; GO
Die Tabelle besitzt einen Clustered Index auf ID; sie ist also nach der ID sortiert. Insgesamt gibt es nur diesen EINEN Index, der die Tabelle selbst ist. Wird also ein Datensatz gelesen, so muss immer die komplette Datenzeile gelesen warden (daher der Hinweis von Andreas auf Columnstore). Wie Du unschwer aus der obigen Struktur entnehmen kannst, hat ein Datensatz eine "Daten"-Lange von 313 Bytes +5 Bytes als Overhead = 318 Bytes.
Auf eine Datenseite können 8.060 Bytes gespeichert warden; somit gehen 25 Datensätze auf eine Datenseite. Die Daten warden also auf ca. 15.000 Datenseiten gespeichert!
Mit der nachfolgenden Abfrage suche ich nach der Messung '00001'
SET STATISTICS IO ON; GO SELECT Id, MessNr FROM dbo.Messungen WHERE MessNr = '00001';
Wenn Du dir das Ergebnis anschaust, werden die grob berechneten 15.000 Datenseiten vollständig durchsucht. Gleiches gilt natürlich, wenn Du statt Einzelwerte einen COUNT auf die Datenmenge ausführst!
SET STATISTICS IO ON; GO SELECT MessNr, COUNT_BIG(*) AS Messungen FROM dbo.Messungen WHERE MessNr = '00001'
GROUP BY MessNr
OPTION (QUERYTRACEON 9130);Die Ursache ist relative einfach zu erklären. In beiden Fallen muss Microsoft SQL Server die vollständige Tabelle durchsuchen, da der EINZIGE Index auf der ID liegt und nicht der MessNr. (Ist so, als ob Du in einem Telefonbuch nach dem Vornamen suchst)
Microsoft SQL Server muss für die Gruppierung erneut die vollständige Tabelle durchsuchen. Fügst Du also weitere Messungen hinzu, vergrößert sich das Volumen der zu durchsuchenden Datenmenge (Es müssen mehr Datenseiten durchsucht werden). Je "breiter" Deine Tabelle ist, umso weniger Datensätze passen auf eine Datenseite und NOCH MEHR Datenseiten müssen gelesen werden.
Also, was macht man in diesem Fall? Man verwendet einen Index, der "schmaler" ist und somit weniger Datenseiten lessen muss. Idealerweise legt man diesen Index auf ein Attribut, nach dem man regelmäßig sucht (in diesem Beispiel also die MEssNr). Ein möglicher Index könnte wie folgt aussehen:
CREATE NONCLUSTERED INDEX ix_Messungen_MessNr ON dbo.Messungen(MessNr); GO
Durch die Erstellung des obigen Index habe ich zwei Fliegen mit einer Klappe geschlagen:
- Auf einer Datenseite können nun MEHR Indexsätze gespeichert werden: 11 Bytes / Datensatz ~ 730 Datensätze
- Das Feld, nach dem ich suche, ist indiziert und ein teurer SCAN kann durch einen SEEK ersetzt werden
Führst Du die obige Abfrage nach der Anzahl der Messungen erneut aus, erhältst Du folgenden Ausführungsplan:
Mit Hilfe eines geeigneten Index hast Du sowohl den Suchalgorithmus als auch das Datenvolumen eingeschränkt. Wäre in dem obigen Beispiel nicht nur ein Clustered Index vorhanden sondern noch ein Index auf dem Feld Col1, so hätte Microsoft SQL Server sich für den Index auf COL1 im ersten Beispiel entschieden, da ja 300 Bytes kleiner sind als 318 Bytes :)
Ich habe zu diesem Thema mal geblogged. Vielleicht hilft Dir das Beispiel im Artikel etwas weiter:
http://db-berater.blogspot.de/2013/06/aggregationsfunktionen-beschleunigen.html
Zumindest zeigt er die Zusammenhänge etwas detaillierter als es diese Antwort vermag!
MCM - SQL Server 2008
MCSE - SQL Server 2012
db Berater GmbH
SQL Server Blog (german only) -
Die letzte ist also die höchste Nummer.
Dann könnte es sein, dass der Optimizer aus dem Index erschließt, dass es wahrscheinlich 0 Sätze zu der Messungsnummer gibt und geht direkt zu einem schlechteren Weg.Hallo Christoph,
das ist aus meiner Sicht eher unwahrscheinlich und ist nur dann problematisch, wenn:
- im SEEK ein Keylookup / RID Lookup generiert wird
- ein JOIN (je nach Datenvolumen) zwischen einemm HASH/MERGE oder NESTED LOOP unterscheidet
- Die Wahl zwischen n Indexe getroffen werden muss
Ansonsten ist das "Ascending Key Problem" hier vollkommen irrelevant!
Liebe Grüße aus Frankfurt, Uwe
MCM - SQL Server 2008
MCSE - SQL Server 2012
db Berater GmbH
SQL Server Blog (german only)- Bearbeitet Uwe RickenMVP Freitag, 29. Januar 2016 13:42
-
Hallo Uwe,
Matthias hatte doch geschrieben: "Ich habe einen Index auf die Spalte "Messungsnummer" gelegt." Damit wäre doch schon Dein Vorschlag abgedeckt, oder?
Ich befürchte, dass wir ohne genauere Kenntnis der Tabellenstruktur und der Indizes wirklich weit kommen.
Einen schönen Tag noch,
Christoph
--
Data Platform MVP - http://www.insidesql.org/blogs/cmuEinen schönen Tag noch, Christoph -- Data Platform MVP - http://www.insidesql.org/blogs/cmu
-
Hallo Uwe,
Matthias hatte doch geschrieben: "Ich habe einen Index auf die Spalte "Messungsnummer" gelegt." Damit wäre doch schon Dein Vorschlag abgedeckt, oder?
Das habe ich doch glatt überlesen! :)
Stimmt, wenn er einen Index auf Messungsnummer hat, dann MUSS dieser Index auch für einen SEEK verwendet werden. Es hilft nichts - wir brauchen den Execution Plan.
Dennoch wird das von Dir erwähnte "Ascending Key" Problem nicht auftreten, da - egal ob 1 oder 1.000.000 DS immer ein Index SEEK verwendet wird. Problematisch wird das Verhalten erst, wenn:
- Speicher für die Abfrage zugewiesen werden muss
- Key Lookups verwendet werden müssen
- JOIN Operatoren (HASH / MERGE) verwendet werden müssen
Da es sich ja nur um ein schlichtes COUNT_BIG() handelt, fällt Option 3 raus. Da der Index auf Messungsnummer ebenfalls gesetzt ist, fällt Option 2 ebenfalls raus.
Bleibt - aktuell - nur Option 1. Da aber ein STREAM AGGREGATE für die Gruppierung verwendet wird (meine Vermutung) scheidet auch Option 1 als Problem aus.
However - warten wir mal ab, was der TE noch an Informationen für uns hat.
MCM - SQL Server 2008
MCSE - SQL Server 2012
db Berater GmbH
SQL Server Blog (german only)