Index für eine fortgesetzte berechnete Spalte nicht suchbar

15

Ich habe eine Tabelle aufgerufen Address, die eine dauerhaft berechnete Spalte namens hat Hashkey. Die Spalte ist deterministisch, aber nicht präzise. Es hat einen eindeutigen Index, der nicht auffindbar ist. Wenn ich diese Abfrage ausführe, wird der Primärschlüssel zurückgegeben:

SELECT @ADDRESSID= ISNULL(AddressId,0)
FROM dbo.[Address]
WHERE HashKey = @HashKey

Ich habe diesen Plan:

BasicPlan

Wenn ich den Index erzwinge, erhalte ich diesen noch schlimmeren Plan:

ForceIndex

Wenn ich versuche, sowohl den Index als auch eine Suche zu erzwingen, erhalte ich eine Fehlermeldung:

Der Abfrageprozessor konnte aufgrund der in dieser Abfrage definierten Hinweise keinen Abfrageplan erstellen. Wiederholen Sie die Abfrage ohne Angabe von Hinweisen und ohne Verwendung vonSET FORCEPLAN

Ist das nur so, weil es nicht präzise ist? Ich dachte, das wäre egal, wenn es bestehen bleibt?

Gibt es eine Möglichkeit, diesen Index suchbar zu machen, ohne dass dies eine nicht berechnete Spalte ist?

Hat jemand Links zu Informationen dazu?

Ich kann die tatsächliche Tabellenerstellung nicht veröffentlichen, aber hier ist eine Testtabelle, die das gleiche Problem aufweist:

drop TABLE [dbo].[Test]

CREATE TABLE [dbo].[Test]
  (
     [test]        [VARCHAR](100) NULL,
     [TestGeocode] [geography] NULL,
     [Hashkey] AS CAST(
                        ( hashbytes
                            ('SHA', 
                                ( RIGHT(REPLICATE(' ', (100)) + isnull([test], ''), ( 100 )) ) 
                                + RIGHT(REPLICATE(' ', (100)) + isnull([TestGeocode].[ToString](), ''), ( 100 ))
                            ) 
                        ) AS BINARY(20)                                                                                                        
                      ) PERSISTED
    CONSTRAINT [UK_Test_HashKey] UNIQUE NONCLUSTERED([Hashkey])
  )    
GO    
DECLARE @Hashkey BINARY(20)

SELECT [Hashkey]
FROM   [dbo].[Test] WITH (FORCESEEK) /*Query processor could not produce a query plan*/
WHERE  [Hashkey] = @Hashkey 
Paul White Monica wieder einsetzen
quelle

Antworten:

12

Das Problem scheint mit der Tatsache zusammenzuhängen, dass [TestGeocode].[ToString]()ein maxDatentyp ( nvarchar(max)) zurückgegeben wird.

Ich stoße auch auf das Problem mit dieser einfacheren Version (Ändern der Definition von c1auf varchar(8000)oder Verwenden, COALESCEanstatt es zu ISNULLbeheben).

DROP TABLE dbo.Test

CREATE TABLE dbo.Test
  (
     c1        VARCHAR(
                          MAX    --Fails
                        --  8000 --Works fine
                          ) NULL,
     comp1 AS CAST(ISNULL(c1, 'ABC') AS VARCHAR(100))
    CONSTRAINT UK_Test_comp1 UNIQUE NONCLUSTERED(comp1)
  )

GO

DECLARE @comp1 VARCHAR(100)

SELECT comp1
FROM   dbo.Test WITH (FORCESEEK)
WHERE  comp1 = @comp1 
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8606); 

Berechnete Spaltenreferenzen werden auf die zugrunde liegende Definition erweitert und später wieder der Spalte zugeordnet. Auf diese Weise können berechnete Spalten abgeglichen werden, ohne sie nach Namen zu referenzieren, und die zugrunde liegenden Definitionen können vereinfacht werden.

ISNULLGibt den Datentyp des ersten Parameters zurück ( VARCHAR(MAX)in meinem Beispiel). Der Rückgabetyp COALESCEwird auch VARCHAR(MAX)hier angegeben, er scheint jedoch anders bewertet zu werden, um das Problem zu vermeiden.

In den Fällen, in denen die Abfrage erfolgreich war, enthält die Ausgabe des Ablaufverfolgungsflags Folgendes

ScaOp_Convert varchar(max) collate 49160,Null,Var,Trim,ML=65535

    ScaOp_Const TI(varchar collate 49160,Var,Trim,ML=3) 
                      XVAR(varchar,Owned,Value=Len,Data = (3,ABC))

Wenn dies fehlschlägt, wird dies durch ersetzt

ScaOp_Identifier COL: ConstExpr1003 

Ich spekuliere, dass in den Fällen, in denen dies fehlschlägt, das (implizite) CAST('ABC' AS VARCHAR(MAX))nur einmal ausgeführt wird und dies als Laufzeitkonstante ausgewertet wird ( weitere Informationen ). Der Verweis auf diese Laufzeitkonstantenbezeichnung verhindert jedoch, dass der tatsächliche Zeichenfolgenliteralwert selbst mit der berechneten Spaltendefinition übereinstimmt.

Durch dieses Umschreiben wird das Problem in Ihrer Abfrage vermieden

CREATE TABLE [dbo].[Test]
  (
     [test]        [VARCHAR](100) NULL,
     [TestGeocode] [geography] NULL,
     [Hashkey] AS CAST(
                        ( hashbytes
                            ('SHA', 
                                ( RIGHT(SPACE(100) + isnull([test], ''), 100) ) 
                                + RIGHT(SPACE(100) + isnull(CAST(RIGHT([TestGeocode].[ToString](),100) AS VARCHAR(100)), ''),100)
                            ) 
                        ) AS BINARY(20)                                                                                                        
                      ) PERSISTED
    CONSTRAINT [UK_Test_HashKey] UNIQUE NONCLUSTERED([Hashkey])
  )
Martin Smith
quelle
0

Diese Symptome treten aufgrund eines nicht sargablen Ausdrucks auf, wenn der Datentyp von @HashKeynicht mit dem der indizierten Spalte übereinstimmt. Möglicherweise benötigen Sie einen expliziten CASTAusdruck in der berechneten Spalte, um den gewünschten Datentyp zu erzwingen.

Aufgrund Ihres Rezepts vermute ich, dass dies ein Fehler ist. Ich habe den Connect-Fehler " Computed Column Index Not Used" mit einer Version von Martins Problemumgehung gemeldet. Fühlen Sie sich frei zu stimmen.

Dan Guzman
quelle