Über den aktuellen SQL-Pass-Newsletter wurde ich auf den absolut interessanten Trick aufmerksam, wie man sehr schnell (im Sinne von "sehr performant") die Anzahl von Datensätzen in einer Tabelle ermittelt. Aber leider ist der Autor dem gleichen Fehler aufgesessen, wie ich zu Beginn meiner SQL-Server-Zeit.
Der erwähnte Trick ist sehr ausführlich im Artikel "Speeding up the Performance of Table Counts in SQL Server 2005" beschrieben. Möglicherweise ist er mit richtig vielen Datensätzen auch wirklich schneller als COUNT, aber ich kann es mir kaum denken. Das ist aber gar nicht so wichtig, weil meine Message ist: Vorsicht vor den geschätzten Kosten. Sie sind nicht richtig.
Wenn ich den Trick ausführe und den COUNT, dann zeigt auch mein Zugriffsplan in den geschätzten Werten an, dass der Trick schneller sei (der Code ist aus dem Artikel abgeleitet, nur leicht vereinfacht):
-- Trick:
SELECT SUM( p.rows )
FROM sys.partitions p
LEFT JOIN sys.allocation_units a
ON p.partition_id = a.container_id
WHERE p.index_id IN (0,1) – 0 heap table , 1 table with clustered index
AND p.rows IS NOT NULL
AND a.type = 1 – row-data only , not LOB
AND p.object_id = object_id('Person.Address')
– Hat "estimated subtree cost" von 0.028
go
– regulär:
SELECT COUNT(*)
FROM Person.Address
– Hat "estimated subtree cost" von 0.055
Wegen der "estimated subtree cost" zeigt das Management-Studio an, dass der erste Batch 33% der Zeit und der zweite 67% in Anspruch nähme. Das stimmt aber gar nicht. Der SQL-Profiler bringt es ans Licht: Der erste Batch dauert 35ms und der zweite nur 9ms. Der Prozentwert im Management-Studio spiegelt nur die geschätzten Kosten wieder. Wenn man die echte Zeit wissen will, dann muss man schon zu Kommissar Profiler gehen… 😉
PS: Schade, dass der Autor weder Kommentare zuließ, noch eine Mail-Adresse angab. Daraus schließe ich, dass er keinen Dialog wünscht. Ist das eine zulässige Annahme?
PPS: Ich sehe gerade, dass sich Steffen Krause ein paar Tage vorher mit dem gleichen Thema beschäftigte. Welch lustige Koinzidenz – er berücksichtigt aber die BLOBs nicht.
Hallo Thomas,
Erst mal – ich brauche die LOBs nicht zu berücksichtigen, weil sys.partitions sie nicht enthält. Die kommen bei Dir aus sys.allocation_units, das ich nicht verwende. Wozu eigentlich dieser Join?
Dass bei Dir ein simples SELECT COUNT(*) schneller ist liegt schlicht daran, dass Person.Address winzig ist. Da ist ein Table Scan tatsächlich billiger als der Join. Es kommt darauf an, was schon im Cache ist und was von der Platte gelesen werden muss.
Aber mach das mal mit einer Tabelle, die ein paar Millionen Zeilen hat und nicht komplett im Hauptspeicher ist…
Gruß,
Steffen
Hallo Steffen,
es ist prima, dass Du Dich meldest. Danke.
Mohamed Hassan beschreibt in seinem Artikel (siehe oben), dass in sys.allocation_units steht, was da drin steht und dann man auf sys.allocation_units.type = 1 filtern müsse, um nur die "in-row data" zu bekommen (und eben nicht die LOBs).
In der Beschreibung zu "sys.partitions.rows" steht lediglich lapidar: "Die ungefähre Anzahl der Zeilen in dieser Partition." Daher war ich sofort bereit zu glauben, dass hier die LOBS mitgezählt werden.
Ich glaube sofort, dass Dein Trick schneller ist als der COUNT. Ist ja auch klar. Btw: stimmen die Werte eigentlich immer?
Ich wollte nur darauf aufmerksam machen, dass die Annahme des Autors allein auf den "estimated subtree costs" basierte, nicht aber auf den tatsächlichen Kosten. Das musste ich leider erst auf die harte Tour lernen… 😉
Viele Grüße
Thomas