El índice en una columna calculada persistente no se puede buscar


15

Tengo una tabla, llamada Address, que tiene una columna calculada persistente llamada Hashkey. La columna es determinista pero no precisa. Tiene un índice único que no se puede buscar. Si ejecuto esta consulta, devolviendo la clave primaria:

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

Me sale este plan:

Plan Básico

Si fuerzo el índice me sale este plan aún peor:

ForceIndex

Si intento forzar tanto el índice como la búsqueda, obtengo un error:

El procesador de consultas no pudo generar un plan de consulta debido a las sugerencias definidas en esta consulta. Vuelva a enviar la consulta sin especificar ninguna pista y sin usarSET FORCEPLAN

¿Es esto solo porque no es preciso? Pensé que no importaba si persistía?

¿Hay alguna manera de hacer que este índice sea buscable sin hacer de esta una columna no calculada?

¿Alguien tiene algún enlace a información sobre esto?

No puedo publicar la creación de la tabla real, pero aquí hay una tabla de prueba que tiene el mismo 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 

Respuestas:


12

El problema parece estar relacionado con el hecho de que [TestGeocode].[ToString]()devuelve un maxtipo de datos ( nvarchar(max)).

También me encuentro con el problema con esta versión más simple (el cambio de la definición de c1a varchar(8000), o usando COALESCEen lugar de ISNULLlo resuelve)

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

Las referencias de columna calculadas se expanden a la definición subyacente y luego se corresponden con la columna más tarde. Esto permite que las columnas calculadas coincidan sin hacer referencia a ellas por nombre y también permite que la simplificación funcione en las definiciones subyacentes.

ISNULLdevuelve el tipo de datos del primer parámetro ( VARCHAR(MAX)en mi ejemplo). El tipo de retorno de COALESCEestará VARCHAR(MAX)aquí también, pero parece evaluarse de manera diferente para evitar el problema.

En los casos en que la consulta tiene éxito, la salida del indicador de rastreo incluye lo siguiente

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

Cuando falla, esto se reemplaza por

ScaOp_Identifier COL: ConstExpr1003 

Yo especulo que en los casos en los que falla la (implícita) CAST('ABC' AS VARCHAR(MAX))se acaba de hacer una vez y esto se evalúa como una constante de tiempo de ejecución ( más información ). Sin embargo, la referencia a esta etiqueta de constante de tiempo de ejecución, en lugar del valor literal de cadena real en sí, evita que coincida con la definición de columna calculada.

Esta reescritura evita el problema en su 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])
  )

0

Obtendrá estos síntomas debido a una expresión no sargable si el tipo de datos de @HashKeyno coincide con el de la columna indexada. Es posible que necesite un explícito CASTen la expresión de columna calculada para forzar el tipo de datos deseado.

Basado en tu repro, sospecho que esto es un error. Archivé un error de conexión, índice de columna calculada no utilizado , con una versión de la solución alternativa de Martin también. Siéntase libre de votar.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.