2012-01-31 109 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

相關問題