Suchen Sie nach einem Prädikat, das nicht alle verfügbaren Spalten verwendet

8

Ich habe ein seltsames Problem bei der Kompilierung von Abfragen, das schwer zu reproduzieren ist. Dies geschieht nur unter hoher Last und kann nicht einfach wiederholt werden.

  • Es gibt eine Tabelle T mit den Spalten A, B, C, D.
  • Es gibt einen nicht eindeutigen Clustered-Index für T (A, B, C, D).
  • Es gibt eine Abfrage SELECT * FROM T WHERE A = @ P1 UND B = @ P2 UND (C = @ P3 ODER C = @ P4) UND D = @ P5. Die Suchbedingung gilt für alle Spalten des Clustered-Index, die 3. Spalte hat ein ODER.

Das Problem ist, dass der Abfrageplan für diese Abfrage nur für A und B das Suchprädikat enthält! Das Prädikat für C und D ist ein gewöhnliches Prädikat. Dies bedeutet, dass der Suchbaum für die Spalten C und D nicht verwendet wird.

Die Datentypen für alle Parameter stimmen mit den Spaltendatentypen überein.

Könnte jemand Hinweise geben, warum dies passieren könnte? SQL-Version ist 2008 R2 (SP1) - 10.50.2789.0 (X64)


quelle
Erhalten Sie jemals einen Plan für eine parametrisierte Abfrage, die eine Gleichheitssuche für alle 4 Spalten durchführt? Wenn ja, verwenden Sie OPTION (RECOMPILE)?
Martin Smith

Antworten:

8

Für eine parametrisierte Abfrage Es können nicht nur zwei Suchvorgänge ausgeführt werden

WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5 

und

WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5 

Denn wenn @P3 = @P4das fälschlicherweise doppelte Zeilen zurückbringen würde. Es würde also einen Operator benötigen, der zuerst Duplikate von diesen entfernt.

Nach einem kurzen Test zu diesem Zweck scheint es von der Größe der Tabelle abhängig zu sein, ob Sie diese erhalten oder nicht. Im Test unten 245/ 246Zeilen ist der Grenzpunkt zwischen Plänen (dies war auch der Grenzpunkt zwischen dem Index, der alle auf einer Seite anpasst und 2 Blattseiten und eine Stammseite wird).

CREATE TABLE T(A INT,B INT,C INT,D INT)

INSERT INTO T
SELECT TOP (245) 1,2,3,5
FROM master..spt_values v1

CREATE CLUSTERED INDEX IX ON T(A, B, C, D)

SELECT index_level,page_count, record_count
FROM sys.dm_db_index_physical_stats(db_id(),object_id('T'),1,NULL, 'DETAILED')

DECLARE @C1 INT = 3,
        @C2 INT = 4

 SELECT * FROM T WHERE A=1 AND B=2 AND (C=@C1 OR C=@C2) AND D=5

 DROP TABLE T

1 Seiten / 245 Zeilen

Dieser Plan hat eine Suche A=1 AND B=2mit einem Restprädikat auf(C=@C1 OR C=@C2) AND D=5

Plan 1

2 Blattseiten / 246 Reihen

Plan 2

Im zweiten Plan sind die zusätzlichen Operatoren dafür verantwortlich, alle Duplikate von den @C1,@C2ersten zu entfernen, bevor die Suche (n) durchgeführt werden.

Die Suche im zweiten Plan ist tatsächlich eine Bereichssuche zwischen A=1 AND B=2 AND C > Expr1010und A=1 AND B=2 AND C < Expr1011mit einem verbleibenden Prädikat D=5. Es ist immer noch keine Gleichstellungssuche für alle 4 Spalten. Weitere Informationen zu den zusätzlichen Planbetreibern finden Sie hier .

Durch Hinzufügen können OPTION (RECOMPILE)die Parameterwerte zur Kompilierungszeit auf Duplikate überprüft werden, und es wird ein Plan mit zwei Gleichheitssuchen erstellt.

Das könnte man auch mit erreichen

;WITH CTE
     AS (SELECT DISTINCT ( C )
         FROM   (VALUES (@C1),
                        (@C2)) V(C))
SELECT CA.*
FROM   CTE
       CROSS APPLY (SELECT *
                    FROM   T
                    WHERE A=1 AND B=2 AND D=5  AND C = CTE.C) CA

Plan 3

In diesem Testfall wäre es jedoch wahrscheinlich kontraproduktiv, wenn zwei Suchvorgänge im Index einer einzelnen Seite und nicht einer das logische E / A erhöhen.

Martin Smith
quelle
1
Habe letzte Nacht einige Tests zu dieser Frage durchgeführt, bevor du deinen ersten Kommentar hinzugefügt hast. Ich bin so weit gekommen, das Verhalten zu sehen, habe aber nicht verstanden, was es verursacht hat (@ P3 = @ P4), also +1 dafür (bereits getan). Ich denke, a select .. union select ...würde Ihnen auch zwei Suchanfragen sowie den zusätzlichen Schritt des Entfernens von Duplikaten aus dem Ergebnis geben.
Mikael Eriksson
1
@MikaelEriksson - SELECT * FROM T WHERE A=1 AND B=2 AND C=@C1 AND D=5 UNION SELECT * FROM T WHERE A=1 AND B=2 AND C=@C2 AND D=5Duplikate, die zurückgegeben werden sollten, konnten jedoch fälschlicherweise entfernt werden. In meinen Beispieldaten, in denen ich alle träge mit dem gleichen Wert bevölkert habe, würde es 1 Zeile nicht zurückgeben256
Martin Smith
2
Ah ja. Und OP hat sogar angegeben, dass der Schlüssel nicht eindeutig ist. Zum Glück habe ich nicht versucht, diese Frage zu beantworten :).
Mikael Eriksson
4

Stimme voll und ganz Martins Analyse zu. Diese Abfrage kann aufgrund des ODER-Prädikats keine Suche für alle 4 Spalten erzeugen, es sei denn (möglicherweise) mit OPTION (RECOMPILE). Ich vermute, dass dies eine Nadel-im-Heu-Stapel-Abfrage ist, Sie möchten wahrscheinlich nicht den zusätzlichen Aufwand.

Wie wäre es damit:

IF @P3=@P4
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5.
ELSE
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P3 AND D=@P5.
    UNION ALL
    SELECT * FROM T WHERE A=@P1 AND B=@P2 AND C=@P4 AND D=@P5.

Ich habe dies nicht getestet, aber der else-Teil sollte 2 Suchvorgänge für alle 4 Werte und eine billige Vereinigung durch Verkettung ergeben. Für den Fall, dass beide Suchvorgänge auf derselben Seite landen, würde ein zusätzliches Lesen der logischen Seite meiner Meinung nach nicht viel Zeit in Anspruch nehmen. Abhängig von Ihren Daten befinden sich die beiden Suchvorgänge jedoch möglicherweise auf unterschiedlichen Seiten.

Vlad G.
quelle