Utilisez la fonction «LEN» dans la clause «WHERE» dans «CREATE UNIQUE INDEX»
12
J'ai ce tableau:
CREATETABLE Table01 (column01 nvarchar(100));
Et je veux créer un index unique sur column01 avec cette condition LEN (column01)> = 5
J'ai essayé:
CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE LEN(column01)>=5;
J'ai eu:
Clause WHERE incorrecte pour l'index filtré 'UIX_01' sur la table 'Table01'.
Et :
ALTERTABLE Table01 ADD column01_length AS(LEN(column01));CREATEUNIQUEINDEX UIX_01 ON Table01(column01)WHERE column01_length >=5;
Produit:
L'index filtré 'UIX_01' ne peut pas être créé sur la table 'Table01' car la colonne 'column01_length' dans l'expression de filtre est une colonne calculée. Réécrivez l'expression de filtre afin qu'elle n'inclue pas cette colonne.
Une méthode pour contourner la restriction d'index filtré consiste à utiliser une vue indexée:
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
ÉDITER:
Comment définir la vue si j'ai deux colonnes dans l'index? CRÉER UN INDEX UNIQUE UIX_01 ON Table01 (colonne01, colonne02) OERE LEN (colonne01)> = 5
L'approche de vue indexée peut être étendue pour une clé composite en ajoutant d'autres colonnes de clé à la définition et à l'index de la vue. Le même filtre est appliqué dans la définition de la vue mais l'unicité des lignes qualifiantes est appliquée par la clé composite plutôt que par la valeur de la colonne unique:
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 Oui, SCHEMABINDINGest requis pour une vue indexée. L'implication est bien sûr que vous devrez supprimer la vue avant de modifier la table. Des outils comme SSDT prendront automatiquement en charge cette dépendance.
Dan Guzman
Comment définir la vue si j'ai deux colonnes dans l'index? CRÉER UN INDEX UNIQUE UIX_01 ON Table01 (colonne01, colonne02) OERE LEN (colonne01)> = 5;
geek
@Jalil, j'ai ajouté un exemple de clé composite à ma réponse.
Dan Guzman
5
Cela semble être une autre des nombreuses limitations des index filtrés. Essayer de le contourner avec LIKEusing WHERE column01 LIKE '_____'ne fonctionne pas non plus, produisant le même message d'erreur ( "clause WHERE incorrecte ..." ).
Outre la VIEWsolution, une autre façon serait de convertir la colonne calculée en une colonne régulière et d'ajouter une CHECKcontrainte pour qu'elle ait toujours des données valides:
Naturellement, cela signifie que vous devez remplir explicitement column01_lengthavec la bonne longueur à chaque fois que vous remplissez column01(sur les insertions et les mises à jour). Cela peut être délicat, car vous devez vous assurer que la longueur est calculée de la même manière que la fonction T-SQL LEN(). En particulier, les espaces de fin doivent être ignorés, ce qui n'est pas nécessairement la façon dont la longueur est calculée par défaut dans divers langages de programmation dans lesquels les applications clientes sont écrites. La logique peut être facile à prendre en compte dans l'appelant, mais vous devez être conscient de la différence en premier lieu.
Une option serait un INSERT/UPDATEdéclencheur 1 pour fournir la valeur correcte pour la colonne, elle apparaît donc comme calculée pour les applications clientes.
1 Comme expliqué dans Déclencheurs par rapport aux contraintes , vous devez utiliser un déclencheur INSTEAD OF pour cela. Un déclencheur AFTER ne s'exécuterait tout simplement jamais, car la longueur absente ferait échouer la contrainte de vérification et, à son tour, empêcherait le déclencheur de s'exécuter. Cependant, les déclencheurs INSTEAD OF ont leurs propres restrictions (voir les directives de planification des déclencheurs DML pour un aperçu rapide).
Je ne sais pas comment cela fonctionnera et il y a peut-être un moyen beaucoup plus facile d'y parvenir que j'ai négligé, mais cela devrait faire ce dont vous avez besoin si vous souhaitez uniquement appliquer l'unicité.
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;
L'utilisation d'une fonction à valeur scalaire dans une contrainte de vérification ou une définition de colonne calculée forcera toutes les requêtes qui touchent la table à s'exécuter en série, même si elles ne font pas référence à la colonne.
Erik Darling
2
@sp_BlitzErik Yep et ce n'est peut-être même pas la pire chose avec cette solution :). Je voulais juste voir si cela fonctionnerait, d'où l'avertissement de performance.
SCHEMABINDING
est requis pour une vue indexée. L'implication est bien sûr que vous devrez supprimer la vue avant de modifier la table. Des outils comme SSDT prendront automatiquement en charge cette dépendance.Cela semble être une autre des nombreuses limitations des index filtrés. Essayer de le contourner avec
LIKE
usingWHERE column01 LIKE '_____'
ne fonctionne pas non plus, produisant le même message d'erreur ( "clause WHERE incorrecte ..." ).Outre la
VIEW
solution, une autre façon serait de convertir la colonne calculée en une colonne régulière et d'ajouter uneCHECK
contrainte pour qu'elle ait toujours des données valides:Testé sur rextester.com
Naturellement, cela signifie que vous devez remplir explicitement
column01_length
avec la bonne longueur à chaque fois que vous remplissezcolumn01
(sur les insertions et les mises à jour). Cela peut être délicat, car vous devez vous assurer que la longueur est calculée de la même manière que la fonction T-SQLLEN()
. En particulier, les espaces de fin doivent être ignorés, ce qui n'est pas nécessairement la façon dont la longueur est calculée par défaut dans divers langages de programmation dans lesquels les applications clientes sont écrites. La logique peut être facile à prendre en compte dans l'appelant, mais vous devez être conscient de la différence en premier lieu.Une option serait un
INSERT/UPDATE
déclencheur 1 pour fournir la valeur correcte pour la colonne, elle apparaît donc comme calculée pour les applications clientes.1 Comme expliqué dans Déclencheurs par rapport aux contraintes , vous devez utiliser un déclencheur INSTEAD OF pour cela. Un déclencheur AFTER ne s'exécuterait tout simplement jamais, car la longueur absente ferait échouer la contrainte de vérification et, à son tour, empêcherait le déclencheur de s'exécuter. Cependant, les déclencheurs INSTEAD OF ont leurs propres restrictions (voir les directives de planification des déclencheurs DML pour un aperçu rapide).
la source
Je ne sais pas comment cela fonctionnera et il y a peut-être un moyen beaucoup plus facile d'y parvenir que j'ai négligé, mais cela devrait faire ce dont vous avez besoin si vous souhaitez uniquement appliquer l'unicité.
la source