Índice em uma coluna computada persistente não procurável

15

Eu tenho uma tabela, chamada Address, que tem uma coluna computada persistida chamada Hashkey. A coluna é determinística, mas não precisa. Ele possui um índice exclusivo que não é procurável. Se eu executar esta consulta, retornando a chave primária:

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

Eu recebo este plano:

BasicPlan

Se eu forçar o índice, recebo esse plano ainda pior:

ForceIndex

Se eu tentar forçar o índice e uma busca, recebo um erro:

O processador de consultas não pôde produzir um plano de consulta devido às dicas definidas nesta consulta. Submeta novamente a consulta sem especificar nenhuma dica e sem usarSET FORCEPLAN

Isso é só porque não é preciso? Eu pensei que não importava se isso persistisse?

Existe uma maneira de tornar esse índice pesquisável sem torná-lo uma coluna não computada?

Alguém tem links para informações sobre isso?

Não consigo postar a criação da tabela real, mas aqui está uma tabela de teste com o mesmo problema:

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 restabelece Monica
fonte

Respostas:

12

O problema parece estar relacionado ao fato de [TestGeocode].[ToString]()retornar um maxtipo de dados ( nvarchar(max)).

Eu também encontrar o problema com esta versão mais simples (mudando a definição de c1para varchar(8000)ou usar COALESCE, em vez de ISNULLresolve-lo)

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

As referências de coluna computadas são expandidas para a definição subjacente e correspondidas novamente à coluna posteriormente. Isso permite que as colunas computadas sejam correspondidas sem referenciá-las pelo nome e também permite que a simplificação opere nas definições subjacentes.

ISNULLretorna o tipo de dados do primeiro parâmetro ( VARCHAR(MAX)no meu exemplo). O tipo de retorno COALESCEseráVARCHAR(MAX) aqui, mas parece ser avaliado de maneira diferente, de maneira a evitar o problema.

Nos casos em que a consulta é bem-sucedida, a saída do sinalizador de rastreamento inclui os seguintes

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

Onde falha, isso é substituído por

ScaOp_Identifier COL: ConstExpr1003 

I especular que, nos casos em que falha a (implícita) CAST('ABC' AS VARCHAR(MAX))é feito apenas uma vez e este é avaliada como uma constante de tempo de execução ( mais informações ). No entanto, a referência a esse rótulo constante de tempo de execução, em vez do próprio valor literal da cadeia real, impede que ele corresponda à definição da coluna calculada.

Essa reescrita evita o problema na sua consulta

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
fonte
0

Você receberá esses sintomas devido a uma expressão não sargável se o tipo de dados @HashKeynão corresponder ao da coluna indexada. Você pode precisar de um explícitoCAST na expressão da coluna computada para coagir o tipo de dados desejado.

Com base no seu repro, suspeito que isso seja um bug. Arquivei um bug do Connect, o Índice de Colunas Computadas Não Usado , com uma versão da solução alternativa de Martin também. Sinta-se livre para votar.

Dan Guzman
fonte