2012-01-31 108 views
5

我有这样一个表,计算列“一类是用作键无效的”:无法添加索引持久化计算列,因为它是

CREATE TABLE PhoneNumbers 
(
    [PhoneNumberID] int identity(1,1) not null primary key clustered, 
    [Number] varchar(20), /* Entire number, like (800) 555-5000 */ 
    [Digits] AS dbo.RegExReplace(Number, '[^0-9]', '') PERSISTED /* Like 8005555000 */ 
) 

它创造精品,和Digits列的预期效果很好,但它似乎不像“PERSISTED”列。当我在WHERE子句中使用Digits进行查询时,它非常慢。当我尝试将索引添加到数字列时,我得到:Column 'Digits' in table 'PhoneNumbers' is of a type that is invalid for use as a key column in an index.

似乎该列并未真正被视为PERSISTED,并且正在每个查询中重新计算,因此不会让我添加索引。

的REGEXREPLACE是定义为一个C#CLR函数如下:

[SqlFunction(IsDeterministic = true, IsPrecise = true)] 
public static SqlString RegExReplace(SqlString expression, SqlString pattern, SqlString replace) 

如何获取Digits列像一个持久列或允许我添加一个索引任何想法?

谢谢!

+1

我不会这样设计的。我不会存储除数字以外的任何内容,并且我会使用前端来执行任何模式。它是过度设计,IMO。 – 2012-01-31 02:50:57

+0

是的,你可能是对的,但是这是一个遗留数据库,它已经有很多很多记录和这些记录的许多入口点。我认为这可能是一个简单的补丁,可以在不需要更改10个不同地方的代码的情况下获取数字。 (“数字”栏实际上是一个新栏,不是原始设计的一部分) – JerSchneid 2012-01-31 03:07:25

回答

6

尝试CAST:

CREATE TABLE PhoneNumbers 
(
    [PhoneNumberID] int identity(1,1) not null primary key clustered, 
    [Number] varchar(20), /* Entire number, like (800) 555-5000 */ 
    [Digits] AS CAST(dbo.RegExReplace(Number, '[^0-9]', '') AS VARCHAR(20)) PERSISTED /* Like 8005555000 */ 
) 

我认为,问题是你的CLR函数返回的SqlString这最终是为nvarchar(4000)或类似的 - 没有索引的。

这是一种已知的“问题”,计算列是从表达式推断数据类型。主要是一个字符串和“辅助函数”的问题,这些函数采用varchar(max),并且在计算精度发生变化时也使用十进制运算。

我有一个小规则,我总是CAST - 它使它明确,避免任何含糊之处。通常,已知很小的列应该显式地小 - varchar(max)似乎具有很多性能开销 - 即使您通过返回varchar(max)并采用varchar(max)的函数,将其转换回你知道的大小,因为它会表现得更好。

+0

哇...真棒回答!实际上,我正在寻找如何将计算列声明为某种类型,但并未考虑简单演员。它实际上解决了“关键列无效”的错误,我可以添加索引,但在该列上搜索时性能仍然很糟糕?我目前正在考虑咬住项目符号并重新设计“数字”列以仅包含数字。非常感谢!你摇滚! – JerSchneid 2012-01-31 03:50:13

+0

@JerSchneid我不确定为什么性能不好 - 当然,请检查您的执行计划。一旦列被保留,它不应该调用你的CLR函数。也许它不是首先使用索引,由于查询中的其他内容?您的索引是否包含持续的列覆盖? – 2012-01-31 04:07:40

+0

执行计划似乎没有给我任何信息...石冷简单的查询,如“来自PhoneNumbers WHERE Digits ='8005552000'的SELECT * FROM”不使用我创建的包含列的任何索引。不知道为什么? – JerSchneid 2012-01-31 07:32:10

相关问题