不可搜索的持久化计算列上的索引


15

我有Address一个名为的表,该表具有一个称为的持久计算列Hashkey。该列是确定性的,但不精确。它具有不可搜索的唯一索引。如果运行此查询,则返回主键:

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

我得到这个计划:

基本计划

如果我强制索引,我会得到更糟糕的计划:

力指数

如果我尝试同时使用索引和查找,则会收到错误消息:

由于此查询中定义的提示,查询处理器无法生成查询计划。重新提交查询而不指定任何提示,也无需使用SET FORCEPLAN

这仅仅是因为它不精确吗?我以为如果坚持下去没关系吗?

有没有一种方法可以使该索引可查询而不将其设为非计算列?

有人在此链接到任何信息吗?

我无法发布实际的表创建,但是这是一个具有相同问题的测试表:

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 

Answers:


12

问题似乎与[TestGeocode].[ToString]()返回max数据类型(nvarchar(max))的事实有关。

我也遇到了这个较简单版本的问题(将c1to 的定义更改为varchar(8000)或使用using COALESCE而不是ISNULL解决它)

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

计算出的列引用将扩展到基础定义,然后再匹配回该列。这允许对计算列进行匹配,而根本不用名称来引用它们,还可以简化对基础定义的操作。

ISNULL返回第一个参数的数据类型(VARCHAR(MAX)在我的示例中)。返回类型也COALESCE将在VARCHAR(MAX)此处,但似乎可以避免该问题,因此对它的评估方式有所不同。

在查询成功的情况下,跟踪标志输出包括以下内容

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

如果失败,将替换为

ScaOp_Identifier COL: ConstExpr1003 

推测,在失败的情况下,(隐式)CAST('ABC' AS VARCHAR(MAX))仅执行一次,并且将其评估为运行时常量(更多信息)。但是,对此运行时常量标签的引用(而不是实际的字符串文字值本身)将阻止它与计算所得的列定义匹配。

此重写避免了查询中的问题

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

如果的数据类型@HashKey与索引列的数据类型不匹配,则由于表达式不可表达,将出现这些症状。您可能需要CAST在计算列表达式中使用显式来强制转换所需的数据类型。

根据您的复制,我怀疑这是一个错误。我还提交了一个Connect错误,即“ 未使用计算列索引”,其中也包含了Martin的变通方法。随时投票。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.