15 index sql-server optimization sql-server-2012 computed-column
我有Address
一个名为 的表,它有一个名为 的持久计算列Hashkey
。该列是确定性的,但不精确。它有一个不可查找的唯一索引。如果我运行这个查询,返回主键:
SELECT @ADDRESSID= ISNULL(AddressId,0)
FROM dbo.[Address]
WHERE HashKey = @HashKey
Run Code Online (Sandbox Code Playgroud)
我得到这个计划:
如果我强制索引,我会得到更糟糕的计划:
如果我尝试同时强制索引和查找,则会出现错误:
由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用的情况下重新提交查询
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
Run Code Online (Sandbox Code Playgroud)
Mar*_*ith 12
问题似乎与[TestGeocode].[ToString]()
返回max
数据类型 ( nvarchar(max)
)的事实有关。
我也遇到了这个更简单版本的问题(更改c1
tovarchar(8000)
或 usingCOALESCE
而不是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);
Run Code Online (Sandbox Code Playgroud)
计算出的列引用会扩展到基础定义,然后再匹配回该列。这允许匹配计算列而根本不按名称引用它们,并且还允许简化对底层定义的操作。
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))
Run Code Online (Sandbox Code Playgroud)
如果失败,则替换为
ScaOp_Identifier COL: ConstExpr1003
Run Code Online (Sandbox Code Playgroud)
我推测在它失败的情况下(隐式)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])
)
Run Code Online (Sandbox Code Playgroud)