it-swarm.com.de

SQL Server-Abfrage: Schnell mit Literal, aber langsam mit Variable

Ich habe eine Ansicht, die 2 Ints aus einer Tabelle mit einem CTE zurückgibt. Wenn ich die Ansicht so abfrage, läuft sie in weniger als einer Sekunde

SELECT * FROM view1 WHERE ID = 1

Wenn ich die Ansicht jedoch so abfrage, dauert es 4 Sekunden.

DECLARE @id INT = 1
SELECT * FROM View1 WHERE ID = @id

Ich habe die zwei Abfragepläne überprüft und die erste Abfrage führt eine gruppierte Indexsuche für die Haupttabelle aus, die 1 Datensatz zurückgibt, und wendet den Rest der Ansichtsabfrage auf diese Ergebnismenge an, während die zweite Abfrage eine Indexsuche durchführt Es werden etwa 3000 Datensätze zurückgegeben, nicht nur der, an dem ich interessiert ist, und das Ergebnisset wird später gefiltert.

Gibt es etwas offensichtliches, das ich vermisse, um zu versuchen, die zweite Abfrage zur Verwendung des Index-Suchvorgangs anstelle eines Index-Scans zu erhalten. Ich benutze SQL 2008, aber alles, was ich mache, muss auch auf SQL 2005 laufen. Zuerst dachte ich, es wäre eine Art Parameter-Sniffing-Problem, aber ich bekomme die gleichen Ergebnisse, auch wenn ich den Cache lösche.

31
Gavin

Möglicherweise liegt dies daran, dass das Optimierungsprogramm im Parameterfall nicht wissen kann, dass der Wert nicht null ist. Daher muss ein Plan erstellt werden, der auch dann korrekte Ergebnisse zurückgibt. Wenn Sie über SQL Server 2008 SP1 verfügen, können Sie versuchen, der Abfrage OPTION(RECOMPILE) hinzuzufügen.

32
erikkallen

In meinem Fall in der DB-Tabelle war der Spaltentyp als VarChar definiert und im parametrisierten Abfrageparametertyp als NVarChar. Dies führte CONVERT_IMPLICIT in den tatsächlichen Ausführungsplan ein, um mit dem Datentyp vor dem Vergleich übereinzustimmen, und dies war ein Grund für die Leistung der Sau, 2 Sekunden vs. 11 Sekunden . Durch das Korrigieren des Parametertyps wurde die parametrisierte Abfrage so schnell wie die nicht parametrisierte Version.

Hoffe, das kann jemandem mit ähnlichen Problemen helfen.

2
Morbia

Sie könnten Ihrer Abfrage ein OPTIMIZE FOR Hinweis hinzufügen, z.

DECLARE @id INT = 1
SELECT * FROM View1 WHERE ID = @id OPTION (OPTIMIZE FOR (@ID = 1))

Wenn SQL den Abfrageplan für die Abfrage mit der Variablen optimiert, wird der verfügbare Index mit der Spalte abgeglichen. In diesem Fall gab es einen Index, so dass SQL den Index nach dem Wert durchsuchen wollte. Wenn SQL den Plan für die Abfrage mit der Spalte und einem Literalwert erstellt hat, kann es die Statistiken und den Wert anzeigen, um zu entscheiden, ob der Index durchsucht werden soll oder ob eine Suche korrekt ist. 

Durch die Verwendung des Optimierungshinweises und eines Werts wird SQL mitgeteilt, dass "dies der Wert ist, der meistens verwendet wird, also für diesen Wert optimieren". Ein Plan wird so gespeichert, als ob dieser Literalwert verwendet wurde. Durch die Verwendung des Optimierungshinweises und des Unterhinweises von UNKNOWN wird SQL mitgeteilt, dass Sie nicht wissen, wie der Wert aussehen wird. Daher prüft SQL die Statistiken für die Spalte und entscheidet, was gesucht oder durchsucht werden soll, und bestimmt den Plan entsprechend. 

1
RC_Cleland

Ich bin auf dieses Problem selbst mit einer Ansicht gestoßen, die <10ms mit einer direkten Zuweisung lief ( WHERE UtilAcctId = 12345 ), aber bei einer Variablenzuweisung ( WHERE UtilAcctId = @UtilAcctId ) mehr als 100 mal so lange dauerte. .
Der Ausführungsplan für Letzteres war nicht anders als wenn ich die Ansicht für die gesamte Tabelle ausgeführt hätte. 

Für meine Lösung waren nicht viele Indizes, Optimierungshinweise oder ein langes Statistik-Update erforderlich. 

Stattdessen habe ich die Ansicht in eine User-Table-Function konvertiert, wobei der Parameter der für die WHERE-Klausel erforderliche Wert war. Tatsächlich war diese WHERE-Klausel 3 Abfragen tief verschachtelt und funktionierte noch immer und sie erreichte wieder die Geschwindigkeit <10ms. 

Irgendwann habe ich den Parameter geändert, um ein TYPE zu sein, der eine Tabelle von UtilAcctIds (int) ist. Dann kann ich die WHERE-Klausel auf eine Liste aus der Tabelle beschränken. WHERE UtilAcctId = [parameter-List] .UtilAcctId. Das funktioniert noch besser. Ich denke, die User-Table-Funktionen sind vorkompiliert.

1
Michael Barash

Ich kam durch dasselbe Problem und es stellte sich heraus, dass es sich um einen fehlenden Index handelt, der einen (linken) Join für das Ergebnis einer Unterabfrage beinhaltet.

select *
from foo A
left outer join (
  select x, count(*)
  from bar
  group by x
) B on A.x = B.x

Ein Index namens bar_x für bar.x wurde hinzugefügt

0
Sandman

DECLARE @id INT = 1

SELECT * FROM View1 WHERE ID = @id

Mach das

DECLARE @sql varchar (max)

SET @ sql = 'SELECT * FROM View1 WHERE ID =' + CAST (@id als varchar)

EXEC (@sql)

Löst dein Problem

0
Neeraj kalyan