1

我相信 SQL Server 索引视图正在错误地验证基表中的插入。

为了模拟,考虑以下

创建表:

CREATE TABLE [dbo].[table_e]
(
    [id] [int] NOT NULL,
    [module] [varchar](50) NULL,
    [event] [varchar](50) NULL,
    [params] [nvarchar](max) NULL,

    CONSTRAINT [PK_table_e] 
        PRIMARY KEY CLUSTERED ([id] ASC)
                    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
                          IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,  
                          ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

填充表

INSERT INTO [dbo].[table_e] ([id], [module], [event], [params])
VALUES (1, 'ModuleB', 'EventT', N'[{"type":"AccountId","value":"AccountX"},{"type":"AccountId","value":"AccountZ"},{"type":"Balance","value":10},{"type":"Balance","value":10}]'),
(2, 'ModuleB', 'EventT', N'[{"type":"AccountId","value":"AccountY"},{"type":"AccountId","value":"AccountX"},{"type":"Balance","value":20}]'),
(3, 'ModuleP', 'EventA', N'[{"type":"AccountId","value":"AccountZ"},{"type":"AccountId","value":"AccountY"},{"type":"Hash","value":"SomeHash"}]')

检查是否一切正常

SELECT * 
FROM [dbo].[table_e] -- returning 3 out of 3

创建模式绑定视图

CREATE VIEW [dbo].[iv_test]
WITH SCHEMABINDING
AS
    SELECT 
        e.[id],
        CAST(JSON_VALUE(e.[params], '$[0].value') AS CHAR(66)) AS [AccountAddress_From],
        CAST(JSON_VALUE(e.[params], '$[1].value') AS CHAR(66)) AS [AccountAddress_To],
        CAST(JSON_VALUE(e.[params], '$[2].value') AS DECIMAL (36)) AS [Amount_Transferred],
        CAST(JSON_VALUE(e.[params], '$[3].value') AS DECIMAL (36)) AS [Amount_Fees]
    FROM 
        [dbo].[table_e] e 
    WHERE 
        e.[module] = 'ModuleB' AND e.[event] = 'EventT'
GO

检查是否一切正常

SELECT * 
FROM [dbo].[iv_test]     -- returning 2 out of 3

清桌子

DELETE FROM [table_e] --3 rows affected

通过创建聚集索引来实现视图:

CREATE UNIQUE CLUSTERED INDEX [PK_iv_test] 
ON [dbo].[iv_test]([id] ASC)
         WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
               SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, 
               DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, 
               ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

现在尝试添加与以前相同的数据

INSERT INTO [dbo].[table_e] ([id], [module], [event], [params])
VALUES (1, 'ModuleB', 'EventT', N'[{"type":"AccountId","value":"AccountX"},{"type":"AccountId","value":"AccountZ"},{"type":"Balance","value":10},{"type":"Balance","value":10}]'),
(2, 'ModuleB', 'EventT', N'[{"type":"AccountId","value":"AccountY"},{"type":"AccountId","value":"AccountX"},{"type":"Balance","value":20}]'),
(3, 'ModuleP', 'EventA', N'[{"type":"AccountId","value":"AccountZ"},{"type":"AccountId","value":"AccountY"},{"type":"Hash","value":"SomeHash"}]')

结果:INSERT不起作用。聚集索引的创建阻止了我INSERT的这个错误:

消息 8115,级别 16,状态 6,第 105
行将 nvarchar 转换为数字数据类型的算术溢出错误。该语句已终止。

这里发生了什么?

看起来应用于索引视图 ege[module_id] = 'Balances' AND e.[event_id] = 'Transfer' 的 where 子句未应用。

因此,所有插入基表的数据都会根据视图 [iv_test] 检查,而在我看来,只有满足视图 [iv_test] 中指定的 WHERE 条件的插入数据才应根据视图中的格式进行检查 [ iv_test]。

有趣的是:以下方法确实有效:

  • 首先插入 ID 为 1 & 2 的记录;
  • 然后创建聚集索引;
  • 然后插入ID为3的记录;

这当然不能解决我的问题,但很明显,在创建聚集索引后,索引视图有一些 WITH NOCHECK 约束处于活动状态。

欢迎任何想法

4

1 回答 1

1

我有一个“解决方案”给你,但没有完整的解释。

在您的索引视图定义中,将最后两个更改casttry_cast

CREATE VIEW [dbo].[iv_test]
WITH SCHEMABINDING
AS
SELECT 
    e.[id]
    ,CAST(JSON_VALUE(e.[params], '$[0].value') AS CHAR(66)) AS [AccountAddress_From]
    ,CAST(JSON_VALUE(e.[params], '$[1].value') AS CHAR(66)) AS [AccountAddress_To]
    ,TRY_CAST(JSON_VALUE(e.[params], '$[2].value') AS DECIMAL (36)) AS [Amount_Transferred]
    ,TRY_CAST(JSON_VALUE(e.[params], '$[3].value') AS DECIMAL (36)) AS [Amount_Fees]
FROM [dbo].[table_e] e 
WHERE e.[module] = 'ModuleB' AND e.[event] = 'EventT'
GO

我知道你在想什么:

但是where视图定义上的子句应该过滤掉cast会失败的行,所以这些不应该被物化,所以物化应该成功

是的,从逻辑上讲,我猜这是对的……这就是为什么在插入第一行之后创建索引(它会很高兴地转换为十进制)然后添加第三行时它起作用的原因。

所以我想推论是,引擎在为多行数据创建过滤索引时执行的操作顺序与在表中插入一行时执行的操作顺序必须有所不同。具体来说,引擎似乎会评估数据的视图输出,然后在决定将什么放入索引时应用谓词。

我敢打赌,保罗怀特可以提供关于幕后究竟发生了什么的细节。

于 2021-09-06T16:43:32.957 回答