这一直困扰着我一段时间,我希望其中一位SQL Server专家能够对此有所了解。
问题是:
索引包含UDT(CLR类型)的SQL Server列时,SQL Server如何确定要对给定查询执行的索引操作?
具体来说,我正在考虑hierarchyid
(AKA SqlHierarchyID
)类型。 Microsoft建议您使用它的方式 - 以及我使用它的方式 - 是:
在hierarchyid
列本身上创建一个索引(让我们称之为ID
)。这样可以进行深度优先搜索,这样当您编写WHERE ID.IsDescendantOf(@ParentID) = 1
时,它就可以执行索引搜索。
创建持久计算的Level
列并在(Level, ID)
上创建索引。这样可以实现广度优先搜索,这样当您编写WHERE ID.GetAncestor(1) = @ParentID
时,它可以为此表达式执行索引搜索(在第二个索引上)。
但我不明白的是这怎么可能?这似乎违反了正常的查询计划规则 - 对GetAncestor
和IsDescendantOf
的调用没有似乎是sargable,所以 应该导致完整的索引扫描,但事实并非如此。显然,并非我抱怨,但我想了解是否可以在我自己的UDT上复制此功能。
hierarchyid
只是一个SQL Server具有特殊意识的“神奇”类型,如果找到某个查询元素和索引的组合,会自动改变执行计划吗?或者SqlHierarchyID
CLR类型是否只是定义了SQL Server引擎可以理解的特殊属性/方法(类似于IsDeterministic
对持久计算列的工作方式)?
我似乎无法找到有关此内容的任何信息。我能够找到的是一个段落,声明IsByteOrdered
属性通过保证每个实例有一个唯一的表示,使索引和检查约束成为可能。虽然这有点有趣,但它没有解释SQL Server如何使用某些实例方法执行 seek 。
再次提出问题 - 索引操作如何适用于hierarchyid
之类的类型,是否可以在新的UDT中获得相同的行为?
答案 0 :(得分:4)
查询优化器团队正在尝试处理不会改变事物顺序的方案。例如,cast(someDateTime as date)
仍然是可以攻击的。我希望随着时间的推移,他们会修复一堆旧的,例如dateadd / datediff和一个常量。
所以......处理Ancestor实际上就像在字符串的开头使用LIKE运算符一样。它不会改变顺序,你仍然可以逃脱。
答案 1 :(得分:2)
你是对的 - HierarchyId和Geometry / Geography都是“魔法”类型,查询优化器能够识别和重写计划以生成优化查询 - 它不像识别可搜索的运算符那么简单。无法模拟与其他UDT的等效行为。
对于HierarchyId,该类型的二进制序列化是特殊的,以便以二进制有序方式表示层次结构。它类似于SQL Xml类型使用的机制,并在研究论文ORDPATHs: Insert-Friendly XML Node Labels中进行了描述。因此,虽然QO规则转换使用IsDescendant和GetAncestor的查询是特殊的,但实际的底层索引是二进制hierarchyid数据的常规关系索引,如果您愿意编写原始查询来执行范围搜索,则可以实现相同的行为而不是调用简单的方法。