如何使索引视图适用于 SQLCLR?

Pxt*_*xtl 2 sql-server materialized-view sql-clr

在 SQL Server 中,索引视图是一个充满限制的地狱。但我需要一个。我有一个格式化程序 SQLCLR 函数,它创建域密钥的美化版本 - 用户希望能够搜索该美化版本的子字符串。因此,我需要持久计算列或物化视图上的全文索引。

但是,格式化程序依赖于存储在多个表中的数据。

因此,这适用于视图,但不适用于持久计算列,因为它们无法从多个表进行查询。

我的 SQLCLR 方法是精确确定的,因此它应该适合在索引视图中使用,但索引视图的索引键中不能包含 SQLCLR。

我可以使用 T-SQL 函数重新实现我的格式化程序FORMAT...但与索引视图不兼容FORMAT

索引视图可以做任何事情吗曾经

如果有人可以向我推荐一份关于最佳实践的好文档,我对“使用触发器滚动你自己的物化视图”方法持开放态度。上次我尝试时,它失去了控制,并且插入和更新代码与初始化之间存在巨大的重复。

我是否缺少某种方法来对几百万行的计算数据进行高性能文本搜索,而不使用物化/索引视图或持久计算列?

格式化程序不执行数据访问。但是,我需要输入格式化程序以使其有用的数据将来自多个表(具有良好的常规联接),因此我无法使用持久计算列来解决此问题。我无法为 SQLCLR 列设置键,因此无法在全文索引中使用它。

我以为这很简单。持久列和索引视图旨在在写入时执行计算,并正确实现观察者模式,以便对其依赖项的更改反映在计算值上。

Jos*_*ell 5

\n

索引视图可以做任何事情吗曾经

\n
\n\n

LEFT JOIN当您将索引视图视为一种解决方案时,却发现您的用例遇到了索引视图的众多限制之一(我正在看着您, ),这实际上有点悲惨。

\n\n

你是对的,你不能将 SQLCLR 包含在索引视图的索引键中。但是,您可以将其包含在SELECT视图定义的列表中,这会将值保留到磁盘。因此,您至少可以避免在从表中读取数据时动态计算值的成本。

\n\n

在 AdventureWorks2014 示例数据库中,我在名称恰当的Person.Person表上创建了此视图:

\n\n
CREATE OR ALTER VIEW dbo.PersonWithHashForSomeReason\nWITH SCHEMABINDING\nAS\nSELECT \n    BusinessEntityID,\n    PersonType,\n    dbo.SpookyHash(CONVERT(binary(50), FirstName)) AS FirstNameHash\nFROM Person.Person\nGO\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意:我懒得写自己的 CLR 函数,所以这个来自这个 Q&A

\n\n

我可以通过将其聚集在以下位置来使其成为索引视图BusinessEntityID

\n\n
CREATE UNIQUE CLUSTERED INDEX CX_BusinessEntityID \nON dbo.PersonWithHashForSomeReason (BusinessEntityID);\nGO\n
Run Code Online (Sandbox Code Playgroud)\n\n

在表中查找特定的行集会产生索引扫描(执行计划链接)。请注意,缺少Compute Scalar通常用于生成哈希值的运算符。由于散列在索引视图中持久保存到磁盘,因此没有必要:

\n\n
SELECT BusinessEntityID, FirstNameHash \nFROM dbo.PersonWithHashForSomeReason WITH (NOEXPAND)\nWHERE FirstNameHash = 0x910C426C533F2C0AAF350158331E3B01;\n
Run Code Online (Sandbox Code Playgroud)\n\n

Plan Explorer 中执行计划的屏幕截图

\n\n

我必须使用NOEXPAND提示才能让它使用视图。

\n\n

您会注意到计划中的警告是由于扫描整个表以查找这些值的事实,因为没有索引键控FirstNameHash

\n\n

不幸的是,尝试对此持久值创建非聚集索引失败:

\n\n
CREATE NONCLUSTERED INDEX IX_FirstNameHash \nON dbo.PersonWithHashForSomeReason (FirstNameHash);\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n

消息 1976,级别 16,状态 1,第 48 行
\n 无法在视图“dbo.PersonWithHashForSomeReason”上创建索引或统计信息“IX_FirstNameHash”,因为无法验证键列“FirstNameHash”是否精确且确定。考虑从索引或统计键中删除列,将列标记为保留在基表中(如果已计算),或者在键中使用非 CLR 派生列。

\n
\n\n

这是因为 SQL Server不信任我们

\n\n
\n

为什么我们不信任\xe2\x80\x99 这个用户?这里真的会出什么问题吗?

\n\n

假设用户对于非确定性函数错误地将自定义属性 IsDeterminsitic 设置为 true,并假设他能够在调用此函数的计算列上创建索引而不保留该索引。[...]这可能会导致索引损坏 因为该函数可能会为相同的输入返回不同的值,因为该函数是不确定的。

\n\n

因此,为了用户\xe2\x80\x99的安全,此版本中的Sql Server要求用户保留计算列(与tsql情况不同)\n以实际索引计算列。

\n
\n\n

我想全文索引也存在同样的限制,尽管我还没有尝试过。

\n\n
\n\n

正如您在自己的帖子中提到的,所有这一切的结果是您被困在这里。实际上你唯一的选择是:

\n\n
    \n
  • 接受表扫描(不理想,根据您的描述甚至可能不实用),或者
  • \n
  • 使用触发器“手动”使该值在普通列中保持最新
  • \n
\n