2012-01-31 3 views
5

Mam tabeli tak z kolumną wyliczona:Nie można dodać indeks utrzymywał obliczane kolumny, ponieważ jest „typu, który jest nieważny do użytku jako klucz”

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

Jest on tworzony w porządku, a kolumna Digits działa świetnie zgodnie z oczekiwaniami, ALE nie zachowuje się jak kolumna "PERSISTED". Kiedy wykonuję zapytanie z Digits w klauzuli WHERE, jest BARDZO spowolnione. Kiedy próbuję dodać indeks do kolumny Cyfry, dostaję: Column 'Digits' in table 'PhoneNumbers' is of a type that is invalid for use as a key column in an index.

Wygląda na to, że kolumna nie jest tak naprawdę traktowana jako PERSISTED i jest przeliczana na każde zapytanie i nie pozwala mi dodać indeksu.

REGEXREPLACE jest C# funkcja CLR zdefiniowane następująco:

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

Wszelkie pomysły, w jaki sposób, aby ta kolumna Digits działać jak kolumny lub utrwalonej pozwala mi dodać indeks ?!

Dziękujemy!

+1

Nie zaprojektowałbym tak. Nie przechowywałbym niczego poza cyframi i używałbym przedniej strony do wymuszania jakichkolwiek wzorów. To jest overdesign, IMO. –

+0

Tak, prawdopodobnie masz rację, ale jest to starsza baza danych, która ma już wiele rekordów i wiele punktów wejścia dla tych rekordów. Pomyślałem, że może to być łatwa łatka, aby uzyskać tylko liczby bez konieczności zmiany kodu w 10 różnych miejscach. (Kolumna "Cyfry" jest w rzeczywistości nową kolumną, a nie częścią oryginalnego projektu) – JerSchneid

Odpowiedz

6

Spróbuj Obsada:

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

Wierzę, że problem jest twoja funkcja CLR wraca SqlString który kończy się nvarchar bytu (4000) lub podobne - nie przestawną.

To rodzaj znanego "problemu" z kolumnami obliczanymi, które typ danych wywnioskowano z wyrażenia. Głównie problem z ciągami i "funkcjami pomocniczymi", które pobierają varchar (max), a także z operacjami dziesiętnymi, w których precyzja zmienia się z powodu obliczeń.

Mam małą regułę, w której zawsze CAST - czyni ją wyraźną i unika wszelkich niejasności. Ogólnie rzecz biorąc, kolumny, które są znane jako małe, powinny być wyraźnie małe - varchar (max) wydaje się mieć duży wpływ na wydajność - nawet jeśli przejdziesz przez funkcję, która zwraca varchar (max) i bierze varchar (max), odsyła z powrotem do rozmiar, który znasz, ponieważ będzie on działał o wiele lepiej.

+0

Wow ... Niesamowita odpowiedź! Właśnie szukałem, jak mógłbym zadeklarować, że kolumna obliczeniowa jest pewnego rodzaju, ale nie brała pod uwagę prostej obsady. To faktycznie rozwiązuje błąd "nieprawidłowy dla klucza kolumny" i mogę dodać indeks, ale wydajność jest nadal straszna podczas wyszukiwania w tej kolumnie? Obecnie rozważam zagryzienie bulletu i przeprojektowanie kolumny "Number" tak, aby zawierała tylko cyfry. Dzięki wielkie! Rządzisz! – JerSchneid

+0

@JerSchneid Nie jestem pewien, dlaczego wydajność jest zła - oczywiście sprawdź plan wykonania. Gdy kolumna zostanie utrwalona, ​​nie powinna wywoływać funkcji CLR. Być może nie używa on najpierw indeksu z powodu innych rzeczy w zapytaniu? Czy Twój indeks zawiera trwałe pokrycie kolumn? –

+0

Plan wykonania nie wydaje mi się żadnych informacji ... Stone cold simple queries, takie jak "SELECT * FROM from PhoneNumbers WHERE Digits =" 8005552000 '"nie używają indeksów, które utworzę, które zawierają kolumnę. Nie wiem dlaczego? – JerSchneid

Powiązane problemy