Index sur une colonne calculée persistante non recherchable


15

J'ai une table, appelée Address, qui a une colonne calculée persistante appelée Hashkey. La colonne est déterministe mais pas précise. Il contient un index unique qui n'est pas recherché. Si j'exécute cette requête, en renvoyant la clé primaire:

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

Je reçois ce plan:

BasicPlan

Si je force l'index, j'obtiens ce plan encore pire:

ForceIndex

Si j'essaie de forcer l'index et une recherche, j'obtiens une erreur:

Le processeur de requêtes n'a pas pu produire un plan de requête en raison des indications définies dans cette requête. Renvoyez la requête sans spécifier d'indices et sans utiliserSET FORCEPLAN

Est-ce juste parce que ce n'est pas précis? Je pensais que ça n'avait pas d'importance si ça persistait?

Existe-t-il un moyen de rendre cet index recherchable sans en faire une colonne non calculée?

Quelqu'un at-il des liens vers des informations à ce sujet?

Je ne peux pas publier la création de table réelle, mais voici une table de test qui a le même problème:

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 

Réponses:


12

Le problème semble être lié au fait que [TestGeocode].[ToString]()renvoie un maxtype de données ( nvarchar(max)).

Je rencontre également le problème avec cette version plus simple (modification de la définition de c1la varchar(8000)ou utiliser au COALESCElieu de le ISNULLrésout)

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); 

Les références de colonne calculées sont développées jusqu'à la définition sous-jacente, puis remises en correspondance avec la colonne ultérieurement. Cela permet de faire correspondre les colonnes calculées sans les référencer par leur nom et permet également à la simplification d'opérer sur les définitions sous-jacentes.

ISNULLrenvoie le type de données du premier paramètre ( VARCHAR(MAX)dans mon exemple). Le type de retour de COALESCEsera VARCHAR(MAX)ici aussi, mais il semble être évalué différemment de manière à éviter le problème.

Dans les cas où la requête réussit, la sortie de l'indicateur de trace comprend les éléments suivants

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))

En cas d'échec, il est remplacé par

ScaOp_Identifier COL: ConstExpr1003 

Je spécule que dans les cas où il échoue, le (implicite) CAST('ABC' AS VARCHAR(MAX))n'est effectué qu'une seule fois et cela est évalué comme une constante d'exécution ( plus d'informations ). Cependant, la référence à cette étiquette de constante d'exécution, au lieu de la valeur littérale de chaîne réelle elle-même, l'empêche de correspondre à la définition de colonne calculée.

Cette réécriture évite le problème dans votre requête

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])
  )

0

Vous obtiendrez ces symptômes en raison d'une expression non sargable si le type de données de @HashKeyne correspond pas à celui de la colonne indexée. Vous pourriez avoir besoin d'un explicite CASTdans l'expression de colonne calculée pour contraindre le type de données souhaité.

Sur la base de votre repro, je soupçonne que c'est un bug. J'ai déposé un bogue Connect, Index des colonnes calculées non utilisé , avec une version de la solution de contournement de Martin également. N'hésitez pas à voter.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.