在“CREATE UNIQUE INDEX”的“WHERE”子句中使用“LEN”函数

gee*_*eek 12 sql-server sql-server-2008-r2 filtered-index

我有这张桌子:

CREATE TABLE Table01 (column01 nvarchar(100));
Run Code Online (Sandbox Code Playgroud)

我想用这个条件LEN(column01) >= 5column01上创建一个唯一索引

我试过:

CREATE UNIQUE INDEX UIX_01 ON Table01(column01) WHERE LEN(column01) >= 5;
Run Code Online (Sandbox Code Playgroud)

我有:

表 'Table01' 上过滤索引 'UIX_01' 的 WHERE 子句不正确。

和 :

ALTER TABLE Table01 ADD column01_length AS (LEN(column01));
CREATE UNIQUE INDEX UIX_01 ON Table01(column01) WHERE column01_length >= 5;
Run Code Online (Sandbox Code Playgroud)

产生:

无法在表 'Table01' 上创建过滤索引 'UIX_01',因为过滤器表达式中的列 'column01_length' 是计算列。重写过滤器表达式,使其不包含此列。

Dan*_*man 15

解决过滤索引限制的一种方法是使用索引视图:

CREATE TABLE dbo.Table01 (
  Column01 NVARCHAR(100)
);
GO

CREATE VIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING AS
SELECT Column01
FROM dbo.Table01
WHERE LEN(Column01) >= 5;
GO

CREATE UNIQUE CLUSTERED INDEX cdx
    ON dbo.vw_Table01_Column01_LenOver5Unique(Column01);
GO

INSERT INTO dbo.Table01 VALUES('1'); --success
INSERT INTO dbo.Table01 VALUES('1'); --success
INSERT INTO dbo.Table01 VALUES('55555'); --success
INSERT INTO dbo.Table01 VALUES('55555'); --duplicate key error
GO
Run Code Online (Sandbox Code Playgroud)

编辑:

如果索引中有两列,我应该如何定义视图?创建唯一索引 UIX_01 ON Table01(column01, column02) WHERE LEN(column01)>=5

通过将其他键列添加到视图定义和索引,可以针对复合键扩展索引视图方法。在视图定义中应用了相同的过滤器,但由复合键而不是单列值强制执行的合格行的唯一性:

CREATE TABLE dbo.Table01 (
   Column01 NVARCHAR(100)
  ,Column02 NVARCHAR(100)
);
GO

CREATE VIEW dbo.vw_Table01_Column01_LenOver5Unique
WITH SCHEMABINDING AS
SELECT Column01, Column02
FROM dbo.Table01
WHERE LEN(Column01) >= 5;
GO

CREATE UNIQUE CLUSTERED INDEX cdx
    ON dbo.vw_Table01_Column01_LenOver5Unique(Column01, Column02)
GO

INSERT INTO dbo.Table01 VALUES('1','A'); --success
INSERT INTO dbo.Table01 VALUES('1','A'); --success
INSERT INTO dbo.Table01 VALUES('55555','A'); --success
INSERT INTO dbo.Table01 VALUES('55555','B'); --success
INSERT INTO dbo.Table01 VALUES('55555','B'); --duplicate key error
GO
Run Code Online (Sandbox Code Playgroud)

  • @Jalil 是的,索引视图需要`SCHEMABINDING`。这当然意味着您需要在更改表之前删除视图。像 SSDT 这样的工具会自动处理这种依赖关系。 (2认同)

ype*_*eᵀᴹ 5

这似乎是过滤索引的许多限制之一。尝试LIKE使用 using绕过它WHERE column01 LIKE '_____'也不起作用,产生相同的错误消息(“不正确的 WHERE 子句...”)。

除了VIEW解决方案,另一种方法是将计算列转换为常规列并添加CHECK约束,使其始终具有有效数据:

CREATE TABLE Table01 (column01 nvarchar(100),
                      column01_length int,
                      CHECK ( column01_length = len(column01)
                              AND column01 IS NOT NULL 
                              AND column01_length IS NOT NULL
                           OR column01 IS NULL 
                              AND column01_length IS NULL )
                     ) ;


CREATE UNIQUE INDEX UIX_01 ON Table01 (column01) WHERE column01_length >= 5 ;
Run Code Online (Sandbox Code Playgroud)

reextester.com 上测试

自然,这意味着您需要在column01_length每次填充时column01(在插入和更新时)显式填充正确的长度。这可能很棘手,因为您需要确保长度的计算方式与 T-SQLLEN()函数的计算方式相同。特别是需要忽略尾随空格,这不一定是编写客户端应用程序的各种编程语言中默认计算长度的方式。 逻辑可能很容易在调用者中说明,但您需要首先意识到差异。

一个选项是INSERT/UPDATE触发器1为列提供正确的值,因此它对客户端应用程序显示为已计算。


1触发器对比约束中所述,您需要为此使用 INSTEAD OF 触发器。AFTER 触发器将永远不会执行,因为缺少的长度会使检查约束失败,进而会阻止触发器运行。但是,INSTEAD OF 触发器有其自身的限制(请参阅DML 触发器规划指南以获得快速概览)。