SELECT COUNT (*) in einer Ansicht ist um Größenordnungen langsamer als SELECT * in derselben Ansicht

7

Die Aussicht

CREATE VIEW [dbo].[vProductList]
WITH SCHEMABINDING
AS 

SELECT
     p.[Id]
    ,p.[Name]
    ,price.[Value] as CalculatedPrice
    ,orders.[Value] as OrdersWithThisProduct
FROM 
    products as p 
    INNER JOIN productMetadata as price ON p.Id = price.ProductId AND price.MetaId = 1
    INNER JOIN productMetadata as orders ON p.Id = orders.ProductId AND orders.MetaId = 2

Nehmen Sie der Einfachheit halber an, dass productMetadataSpalten ProductId, MetaId, Value~ 87 m Zeilen haben und die productsTabelle ungefähr 400.000 Zeilen enthält .

Allgemeine Abfragen gegen diese Ansicht funktionieren einwandfrei:

SELECT * FROM vProductList WHERE CalculatedPrice > 500

Abfrageergebnisse in 2-4 Sekunden (über einen VPN und eine Fernbedienung, also bin ich gut damit).

Das Ändern der obigen Werte in eine Zählung ist ebenso schnell:

SELECT COUNT(*) from vProductList WHERE CalculatedPrice > 500

Läuft ungefähr zur gleichen Zeit wie die Rohauswahl, mit der ich wieder einverstanden bin. Es gibt ungefähr 10.000 Produkte, die diese Kriterien erfüllen.

Ich bin auf zwei verschiedene Fälle gestoßen, in denen die Dinge wirklich seltsam werden und für immer dauern.

Zuerst

Durchführen einer Abfrage für eine der Spalten aus der Basistabelle in der Ansicht:

SELECT * FROM vProductList WHERE Name = 'Hammer' 

Die Ausführung dieser Abfrage dauert einige Zeit (20 bis 30 Sekunden) und liefert ~ 30.000 Ergebnisse. Eine geringfügige Änderung dieser Abfrage:

SELECT COUNT(*) FROM vProductList WHERE Name = 'Hammer' 

Es dauert dreizehn Minuten, um eine Zählung mit ~ 30k zurückzugeben.

Zweite

Doing eine WHERE INUnterabfrage

SELECT * FROM vProductList WHERE Id IN (SELECT ProductId FROM TableThatHasFKToProductId and ColumnInTable = 'Yes')

Diese Abfrage gibt ~ 300.000 Zeilen zurück und dauert zwei Minuten, um zurückzukehren (ein Großteil dieser Zeit wird meines Erachtens einfach für das Herunterladen der Daten in SSMS aufgewendet). Wenn Sie dies jedoch SELECT COUNT(*)in eine Abfrage ändern , dauert die Abfrage 20 Minuten.

SELECT COUNT(*) FROM vProductList WHERE Id IN (SELECT ProductId FROM TableThatHasFKToProductId and ColumnInTable = 'Yes')

Warum ist es SELECT *schneller als SELECT COUNT?

Ich verwende die von SSMS bereitgestellte Gesamtausführungszeit für alle hier aufgeführten Zeiten.

Ausführungspläne

Planen für SELECT 1 FROM v WHERE IN (...)

Planen für SELECT COUNT(0) FROM v WHERE IN (...)

Hinweis: Ich habe versucht, PasteThePlan zu verwenden, aber es wurde mir immer wieder mitgeteilt, dass der Plan eine ungültige XML-Datei ist.

Nate
quelle

Antworten:

5

Aus den bereitgestellten Ausführungsplänen COUNTwählt der Optimierer für den Fall eine lokale / globale Aggregationsstrategie für den endgültigen Join aus:

Final Join

Leider überschätzt der Optimierer die Effektivität der lokalen Aggregation. Es werden 136 Zeilen geschätzt, die den Join der verschachtelten Schleifen steuern, während zur Laufzeit 366.115 auftreten.

Schätzungen

Die 366.115 Indexsuchen sind für eine lokale SQL Server-Instanz möglicherweise kein allzu großes Problem. Die im Plan enthaltenen Wartestatistiken zeigen jedoch die E / A- (und möglicherweise Speicher-) Einschränkungen Ihrer aktuellen Azure SQL-Datenbankkonfiguration:

Warte Statistiken

Der Plan für SELECT 1zeigt eine ausschließlich Hash- und Merge-Join-Strategie, die in diesem Fall mit sehr begrenztem Speicher und / oder E / A-Funktionen zu besseren Ergebnissen führt.

Möglicherweise sehen Sie bei der ersten Abfrage mit einem OPTION (HASH JOIN, MERGE JOIN)Hinweis eine bessere Leistung , aber das grundlegende Problem ist die schlechte Schätzung der Kardinalität / Datenverteilung, die durch die große Anzahl von Verknüpfungen verursacht wird.

Lassen Sie sich nicht von den Kostenprozentsätzen irreführen, die für jeden Planbetreiber angezeigt werden. Diese Zahlen werden derzeit aus der Kostenschätzung des Optimierers abgeleitet (unter Verwendung eines abstrakten Modells). Die Zahlen geben keine Laufzeitbedingungen oder Kosten wieder.

Große Abweichungen zwischen geschätzten und tatsächlichen Zeilenzahlen können häufig zu Problemen führen. Dies gilt insbesondere für eine Unterschätzung, die dazu führt, dass der Optimierer eine Strategie wählt, die bei einem bestimmten Hardware-Setup nicht gut skaliert werden kann.

Paul White 9
quelle