Verwenden Sie die LEN-Funktion in der WHERE-Klausel in CREATE UNIQUE INDEX.
12
Ich habe diesen Tisch:
CREATETABLE Table01 (column01 nvarchar(100));
Und ich möchte einen eindeutigen Index für column01 mit dieser Bedingung LEN (column01)> = 5 erstellen
Ich habe es versucht:
CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE LEN(column01)>=5;
Ich habe:
Falsche WHERE-Klausel für den gefilterten Index 'UIX_01' in Tabelle 'Table01'.
Und :
ALTERTABLE Table01 ADD column01_length AS(LEN(column01));CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE column01_length >=5;
Erzeugt:
Der gefilterte Index 'UIX_01' kann für die Tabelle 'Table01' nicht erstellt werden, da die Spalte 'column01_length' im Filterausdruck eine berechnete Spalte ist. Schreiben Sie den Filterausdruck so um, dass er diese Spalte nicht enthält.
Eine Methode, um die Einschränkung des gefilterten Index zu umgehen, ist eine indizierte Sicht:
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
BEARBEITEN:
Wie definiere ich die Ansicht, wenn der Index zwei Spalten enthält? CREATE UNIQUE INDEX UIX_01 ON Table01 (Spalte01, Spalte02) WHERE LEN (Spalte01)> = 5
Der Ansatz der indizierten Ansicht kann für einen zusammengesetzten Schlüssel erweitert werden, indem der Ansichtsdefinition und dem Index weitere Schlüsselspalten hinzugefügt werden. Derselbe Filter wird in der Ansichtsdefinition angewendet, aber die Eindeutigkeit der qualifizierenden Zeilen wird durch den zusammengesetzten Schlüssel und nicht durch den Einzelspaltenwert erzwungen:
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
Und ich erwarte, dass dies viel besser funktioniert als meine Monstrosität.
James Anderson
@ Dan Guzman sollte ich 'WITH SCHEMABINDING' verwenden?
Aussenseiter
2
@Jalil Ja, SCHEMABINDINGist für eine indizierte Ansicht erforderlich. Die Implikation ist natürlich, dass Sie die Ansicht löschen müssen, bevor Sie die Tabelle ändern. Werkzeuge wie SSDT kümmern sich automatisch um diese Abhängigkeit.
Dan Guzman
Wie definiere ich die Ansicht, wenn der Index zwei Spalten enthält? CREATE UNIQUE INDEX UIX_01 ON Table01 (Spalte01, Spalte02) WHERE LEN (Spalte01)> = 5;
Aussenseiter
@ Jalil, ich habe meiner Antwort ein zusammengesetztes Schlüsselbeispiel hinzugefügt.
Dan Guzman
5
Dies scheint eine weitere der vielen Einschränkungen von gefilterten Indizes zu sein. Der Versuch, es mit LIKEusing zu umgehen , WHERE column01 LIKE '_____'funktioniert ebenfalls nicht und erzeugt dieselbe Fehlermeldung ( "Incorrect WHERE-Klausel ..." ).
Neben der VIEWLösung besteht eine andere Möglichkeit darin, die berechnete Spalte in eine reguläre Spalte umzuwandeln und eine CHECKEinschränkung hinzuzufügen , damit sie immer gültige Daten enthält:
Das bedeutet natürlich, dass Sie column01_lengthbei jedem Auffüllen column01(bei Einfügungen und Aktualisierungen) explizit die richtige Länge angeben müssen. Dies kann schwierig sein, da Sie sicherstellen müssen, dass die Länge auf dieselbe Weise berechnet wird wie in T-SQLLEN() Funktion. Insbesondere müssen die nachgestellten Leerzeichen ignoriert werden. Dies muss nicht unbedingt der Fall sein, wenn die Länge in verschiedenen Programmiersprachen, in denen Clientanwendungen geschrieben sind, standardmäßig berechnet wird erstens bewusst den Unterschied.
Eine Option wäre ein INSERT/UPDATETrigger 1 , um den korrekten Wert für die Spalte bereitzustellen, sodass diese für Clientanwendungen als berechnet angezeigt wird.
1 Wie in Trigger im Vergleich zu Constraints erläutert , müssten Sie hierfür einen INSTEAD OF-Trigger verwenden. Ein AFTER-Trigger würde einfach niemals ausgeführt, da die fehlende Länge die Prüfbedingung nicht erfüllen würde und dies wiederum die Ausführung des Triggers verhindern würde. STATT Trigger haben jedoch ihre eigenen Einschränkungen ( eine schnelle Übersicht finden Sie in den DML-Trigger-Planungsrichtlinien ).
Ich bin nicht sicher, wie sich dies auswirken wird, und es gibt möglicherweise einen viel einfacheren Weg, dies zu erreichen, den ich übersehen habe, aber dies sollte tun, was Sie brauchen, wenn Sie nur an der Durchsetzung der Eindeutigkeit interessiert sind.
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;
Die Verwendung einer skalarwertigen Funktion in einer Prüfbedingung oder einer berechneten Spaltendefinition zwingt alle Abfragen, die die Tabelle berühren, zur seriellen Ausführung, auch wenn sie nicht auf die Spalte verweisen.
Erik Darling
2
@sp_BlitzErik Yep und das ist vielleicht nicht einmal das Schlimmste an dieser Lösung :). Ich wollte nur sehen, ob es funktionieren würde, daher die Leistungswarnung.
SCHEMABINDING
ist für eine indizierte Ansicht erforderlich. Die Implikation ist natürlich, dass Sie die Ansicht löschen müssen, bevor Sie die Tabelle ändern. Werkzeuge wie SSDT kümmern sich automatisch um diese Abhängigkeit.Dies scheint eine weitere der vielen Einschränkungen von gefilterten Indizes zu sein. Der Versuch, es mit
LIKE
using zu umgehen ,WHERE column01 LIKE '_____'
funktioniert ebenfalls nicht und erzeugt dieselbe Fehlermeldung ( "Incorrect WHERE-Klausel ..." ).Neben der
VIEW
Lösung besteht eine andere Möglichkeit darin, die berechnete Spalte in eine reguläre Spalte umzuwandeln und eineCHECK
Einschränkung hinzuzufügen , damit sie immer gültige Daten enthält:Getestet bei rextester.com
Das bedeutet natürlich, dass Sie
column01_length
bei jedem Auffüllencolumn01
(bei Einfügungen und Aktualisierungen) explizit die richtige Länge angeben müssen. Dies kann schwierig sein, da Sie sicherstellen müssen, dass die Länge auf dieselbe Weise berechnet wird wie in T-SQLLEN()
Funktion. Insbesondere müssen die nachgestellten Leerzeichen ignoriert werden. Dies muss nicht unbedingt der Fall sein, wenn die Länge in verschiedenen Programmiersprachen, in denen Clientanwendungen geschrieben sind, standardmäßig berechnet wird erstens bewusst den Unterschied.Eine Option wäre ein
INSERT/UPDATE
Trigger 1 , um den korrekten Wert für die Spalte bereitzustellen, sodass diese für Clientanwendungen als berechnet angezeigt wird.1 Wie in Trigger im Vergleich zu Constraints erläutert , müssten Sie hierfür einen INSTEAD OF-Trigger verwenden. Ein AFTER-Trigger würde einfach niemals ausgeführt, da die fehlende Länge die Prüfbedingung nicht erfüllen würde und dies wiederum die Ausführung des Triggers verhindern würde. STATT Trigger haben jedoch ihre eigenen Einschränkungen ( eine schnelle Übersicht finden Sie in den DML-Trigger-Planungsrichtlinien ).
quelle
Ich bin nicht sicher, wie sich dies auswirken wird, und es gibt möglicherweise einen viel einfacheren Weg, dies zu erreichen, den ich übersehen habe, aber dies sollte tun, was Sie brauchen, wenn Sie nur an der Durchsetzung der Eindeutigkeit interessiert sind.
quelle