SQL用户定义类型(UDT)中的索引是如何工作的?

5
这个问题困扰了我一段时间,希望 SQL Server 专家能够解答。问题是:当你对包含 UDT(CLR 类型)的 SQL Server 列建立索引时,SQL Server 如何确定要执行哪个索引操作以适应给定的查询?具体来说,我想到的是 hierarchyid(也称为 SqlHierarchyID)类型。Microsoft 建议的使用方式 - 以及我使用的方式 - 是:
1. 在 hierarchyid 列本身上创建一个索引(我们称之为 ID)。这样可以进行深度优先搜索,因此当您编写 WHERE ID.IsDescendantOf(@ParentID) = 1 时,它可以执行索引查找。
2. 创建一个持久化计算的 Level 列并在 (Level, ID) 上创建一个索引。这将启用广度优先搜索,因此当您编写 WHERE ID.GetAncestor(1) = @ParentID 时,它可以执行索引查找 (在第二个索引上)。
但是我不明白的是,这是如何实现的?它似乎违反了正常的查询计划规则 - 对 GetAncestor 和 IsDescendantOf 的调用似乎并不可搜索,因此这应该导致完全的索引扫描,但事实并非如此。显然我不会抱怨,但我正在尝试理解是否可以在自己的 UDT 上复制此功能。hierarchyid 是否只是 SQL Server 具有特殊意识的“神奇”类型,并且如果发现某个查询元素和索引的特定组合,它会自动更改执行计划?还是 SqlHierarchyID CLR 类型只是定义了一些特殊属性/方法(类似于持久化计算列的 IsDeterministic 如何工作)被 SQL Server 引擎理解?
我似乎找不到任何关于这个问题的信息。我能找到的所有信息都是一个段落,说明 IsByteOrdered 属性通过保证每个实例具有一个唯一表示,使索引和检查约束等功能成为可能;虽然这有点有趣,但它并不能解释 SQL Server 如何能够使用某些实例方法执行查找。
所以问题又来了 - 对于 hierarchyid 等类型,索引操作是如何工作的?是否可以在新的 UDT 中获得相同的行为?
2个回答

5

查询优化器团队正在尝试处理不改变顺序的场景。例如,cast(someDateTime as date) 仍然是可搜索的。我希望随着时间的推移,他们会修复很多旧问题,例如带有常量的dateadd/datediff。

所以... 处理祖先节点就像使用具有字符串开头的LIKE运算符一样有效。它不改变顺序,你仍然可以做很多事情。


CAST在某些情况下是可搜索的,即使在SQL 2005中也是如此。LEFT仍然不是,但是当通配符位于末尾时,LIKE就可以提供LEFT的功能。 - Rob Farley
看起来证据支持你的观点。这对我来说太糟糕了,因为这意味着没有办法向优化器提供有关如何对我的UDT方法或属性进行sarg的提示。唉,也许在某个遥远的未来版本中会有改善。 - Aaronaught
1
是的,这不是你自己可以设置的东西。另外,我在http://msmvps.com/blogs/robfarley/archive/2010/01/22/sargable-functions-in-sql-server.aspx写了一篇博客文章。 - Rob Farley
更新链接:http://blogs.msmvps.com/robfarley/2010/01/21/sargable-functions-in-sql-server/ - Markus Jarderot
啊,没错 - msmvps.com 现在改成了 WordPress,看起来很多日期都变了(因为我在澳大利亚,而转换是在美国时区或其他什么地方完成的)。唉... - Rob Farley
显示剩余2条评论

3
您是正确的——HierarchyId和Geometry/Geography都是“神奇”的类型,查询优化器能够识别并重写计划以生成优化的查询——这不仅仅是识别可搜索操作符那么简单。没有办法用其他UDT模拟等效行为。
对于HierarchyId,该类型的二进制序列化是特殊的,以便以二进制有序方式表示分层结构。它类似于SQL Xml类型使用的机制,并在一篇研究论文中描述了ORDPATHs: Insert-Friendly XML Node Labels。因此,虽然用于转换使用IsDescendant和GetAncestor的查询的QO规则是特殊的,但实际底层索引是对二进制hierarchyid数据的常规关系索引,如果您愿意编写原始查询以执行范围查找而不是调用简单方法,则可以实现相同的行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接