Użyj funkcji „LEN” w klauzuli „WHERE” w „CREATE UNIQUE INDEX”
12
Mam ten stół:
CREATETABLE Table01 (column01 nvarchar(100));
I chcę utworzyć unikalny indeks na kolumnie 01 z tym warunkiem LEN (kolumna01)> = 5
Próbowałem:
CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE LEN(column01)>=5;
Mam:
Niepoprawna klauzula WHERE dla filtrowanego indeksu „UIX_01” w tabeli „Table01”.
I :
ALTERTABLE Table01 ADD column01_length AS(LEN(column01));CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE column01_length >=5;
Produkuje:
Filtrowanego indeksu „UIX_01” nie można utworzyć w tabeli „Table01”, ponieważ kolumna „column01_length” w wyrażeniu filtru jest kolumną obliczoną. Przepisz wyrażenie filtru, aby nie zawierało tej kolumny.
Jedną z metod obejścia filtrowanego ograniczenia indeksu jest widok indeksowany:
CREATETABLE dbo.Table01 (
Column01 NVARCHAR(100));
GO
CREATEVIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING ASSELECT Column01
FROM dbo.Table01
WHERE LEN(Column01)>=5;
GO
CREATEUNIQUECLUSTEREDINDEX cdx
ON dbo.vw_Table01_Column01_LenOver5Unique(Column01);
GO
INSERTINTO dbo.Table01 VALUES('1');--successINSERTINTO dbo.Table01 VALUES('1');--successINSERTINTO dbo.Table01 VALUES('55555');--successINSERTINTO dbo.Table01 VALUES('55555');--duplicate key error
GO
EDYTOWAĆ:
Jak zdefiniować widok, jeśli mam dwie kolumny w indeksie? UTWÓRZ UNIKALNY INDEKS UIX_01 NA Table01 (column01, column02) GDZIE DŁUGOŚĆ (column01)> = 5
Podejście do widoku indeksowanego można rozszerzyć dla klucza złożonego, dodając inne kolumny kluczy do definicji widoku i indeksu. Ten sam filtr jest stosowany w definicji widoku, ale unikalność kwalifikujących się wierszy jest wymuszana przez klucz złożony zamiast wartości pojedynczej kolumny:
CREATETABLE dbo.Table01 (
Column01 NVARCHAR(100),Column02 NVARCHAR(100));
GO
CREATEVIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING ASSELECT Column01, Column02
FROM dbo.Table01
WHERE LEN(Column01)>=5;
GO
CREATEUNIQUECLUSTEREDINDEX cdx
ON dbo.vw_Table01_Column01_LenOver5Unique(Column01, Column02)
GO
INSERTINTO dbo.Table01 VALUES('1','A');--successINSERTINTO dbo.Table01 VALUES('1','A');--successINSERTINTO dbo.Table01 VALUES('55555','A');--successINSERTINTO dbo.Table01 VALUES('55555','B');--successINSERTINTO dbo.Table01 VALUES('55555','B');--duplicate key error
GO
@Jalil Tak, SCHEMABINDINGjest wymagany dla widoku indeksowanego. Implikacją jest oczywiście to, że trzeba będzie zmienić widok przed zmianą tabeli. Narzędzia takie jak SSDT automatycznie zajmą się tą zależnością.
Dan Guzman,
Jak zdefiniować widok, jeśli mam dwie kolumny w indeksie? UTWÓRZ UNIKALNY INDEKS UIX_01 NA Table01 (column01, column02) WHERE LEN (column01)> = 5;
maniak
@Jilil, dodałem złożony kluczowy przykład do mojej odpowiedzi.
Dan Guzman
5
To wydaje się być kolejnym z wielu ograniczeń filtrowanych indeksów. Próba obejścia go przy LIKEużyciu WHERE column01 LIKE '_____'również nie działa, co powoduje wyświetlenie tego samego komunikatu o błędzie ( „Niepoprawna klauzula WHERE ...” ).
Oprócz VIEWrozwiązania innym sposobem byłoby przekonwertowanie kolumny obliczanej na zwykłą i dodanie CHECKograniczenia, tak aby zawsze zawierało prawidłowe dane:
Oczywiście oznacza to, że musisz jawnie wypełnić column01_lengthodpowiednią długość przy każdym wypełnianiu column01(w przypadku wstawek i aktualizacji). Może to być trudne, ponieważ musisz upewnić się, że długość jest obliczana w taki sam sposób, jak LEN()robi to funkcja T-SQL . W szczególności należy pominąć końcowe spacje, co niekoniecznie jest domyślnym sposobem obliczania długości w różnych językach programowania, w których zapisywane są aplikacje klienckie. Logika może być łatwa do uwzględnienia w programie wywołującym, ale musisz być świadomy różnicy w pierwszej kolejności.
Opcją może być INSERT/UPDATEwyzwalacz 1, który poda poprawną wartość dla kolumny, więc wygląda na obliczoną dla aplikacji klienckich.
1 Jak wyjaśniono w Wyzwalacze w porównaniu do ograniczeń , w tym celu należy użyć wyzwalacza INSTEAD OF. Wyzwalacz PO, po prostu nigdy się nie wykona, ponieważ nieobecna długość nie spełni ograniczenia sprawdzania, a to z kolei uniemożliwi uruchomienie wyzwalacza. INSTEAD OF wyzwalaczy ma jednak swoje własne ograniczenia (zobacz Krótki przewodnik dotyczący planowania wyzwalaczy DML ).
Nie jestem pewien, jak to się sprawdzi i może istnieć o wiele łatwiejszy sposób na osiągnięcie tego, co przeoczyłem, ale powinno to zrobić wszystko, czego potrzebujesz, jeśli chcesz egzekwować wyjątkowość.
CREATETABLE dbo.Table01
(
Column01 NVARCHAR(100));
GO
CREATEFUNCTION dbo.ChkUniqueColumn01OverLen5()
RETURNS BIT
ASBEGINDECLARE@Result BIT,@Count BIGINT,@DistinctCount BIGINT
SELECT@Count = COUNT(Column01),@DistinctCount = COUNT(DISTINCT Column01)FROM Table01
WHERE LEN(Column01)>=5SELECT@Result =CASEWHEN@Count =@DistinctCount THEN1ELSE0ENDRETURN@Result
END;
GO
ALTERTABLE dbo.Table01
ADDCONSTRAINT Chk_UniqueColumn01OverLen5
CHECK(dbo.ChkUniqueColumn01OverLen5()=1);
GO
INSERT dbo.Table01 (Column01)VALUES(N'123'),(N'1234');
GO
INSERT dbo.Table01 (Column01)VALUES(N'12345');
GO
INSERT dbo.Table01 (Column01)VALUES(N'12345');-- Will fail
GO
INSERT dbo.Table01 (Column01)VALUES(N'123');-- Will pass
GO
UPDATE dbo.Table01
SET Column01 ='12345'WHERE Column01 ='1234'-- Will fail
GO
SELECT*FROM dbo.Table01;
GO
DROPTABLE Table01;DROPFUNCTION dbo.ChkUniqueColumn01OverLen5;
Użycie funkcji o wartości skalarnej w ograniczeniu sprawdzania lub definicji kolumny obliczonej zmusi wszystkie zapytania dotykające tabeli do uruchomienia szeregowego, nawet jeśli nie odwołują się do kolumny.
Erik Darling,
2
@sp_BlitzErik Tak, a to może nie być najgorsze w tym rozwiązaniu :). Chciałem tylko zobaczyć, czy to zadziała, stąd ostrzeżenie o wydajności.
SCHEMABINDING
jest wymagany dla widoku indeksowanego. Implikacją jest oczywiście to, że trzeba będzie zmienić widok przed zmianą tabeli. Narzędzia takie jak SSDT automatycznie zajmą się tą zależnością.To wydaje się być kolejnym z wielu ograniczeń filtrowanych indeksów. Próba obejścia go przy
LIKE
użyciuWHERE column01 LIKE '_____'
również nie działa, co powoduje wyświetlenie tego samego komunikatu o błędzie ( „Niepoprawna klauzula WHERE ...” ).Oprócz
VIEW
rozwiązania innym sposobem byłoby przekonwertowanie kolumny obliczanej na zwykłą i dodanieCHECK
ograniczenia, tak aby zawsze zawierało prawidłowe dane:Testowany na rextester.com
Oczywiście oznacza to, że musisz jawnie wypełnić
column01_length
odpowiednią długość przy każdym wypełnianiucolumn01
(w przypadku wstawek i aktualizacji). Może to być trudne, ponieważ musisz upewnić się, że długość jest obliczana w taki sam sposób, jakLEN()
robi to funkcja T-SQL . W szczególności należy pominąć końcowe spacje, co niekoniecznie jest domyślnym sposobem obliczania długości w różnych językach programowania, w których zapisywane są aplikacje klienckie. Logika może być łatwa do uwzględnienia w programie wywołującym, ale musisz być świadomy różnicy w pierwszej kolejności.Opcją może być
INSERT/UPDATE
wyzwalacz 1, który poda poprawną wartość dla kolumny, więc wygląda na obliczoną dla aplikacji klienckich.1 Jak wyjaśniono w Wyzwalacze w porównaniu do ograniczeń , w tym celu należy użyć wyzwalacza INSTEAD OF. Wyzwalacz PO, po prostu nigdy się nie wykona, ponieważ nieobecna długość nie spełni ograniczenia sprawdzania, a to z kolei uniemożliwi uruchomienie wyzwalacza. INSTEAD OF wyzwalaczy ma jednak swoje własne ograniczenia (zobacz Krótki przewodnik dotyczący planowania wyzwalaczy DML ).
źródło
Nie jestem pewien, jak to się sprawdzi i może istnieć o wiele łatwiejszy sposób na osiągnięcie tego, co przeoczyłem, ale powinno to zrobić wszystko, czego potrzebujesz, jeśli chcesz egzekwować wyjątkowość.
źródło