Yan*_*tle 6 sql-server execution-plan sql-server-2019
我有具有树结构的表(由hierarchyid列定义),我想选择特定记录的所有后代。为此,我正在使用hiearchyid.IsDescendantOf()方法。
我预计,由于我不进行简单的比较,但我正在执行操作(在本例中我调用该IsDescendantOf()方法),那么我将得到一些带有索引扫描等的可怕执行计划。
然而,SQL Server 将其优化为漂亮的小索引查找。
我很困惑为什么以及如何。
CLR 类型上的调用方法通常会被优化吗?我假设 SQL Server 将 CLR 类型视为不透明的黑匣子,因此无法发挥其魔力。(因为它也无法在本机 SQL 函数上执行此操作。)
或者这仅适用于该特定方法?(由于这些hieararchyid值是按深度优先排序的,因此我只需通过比较就可以获得类似的结果。)
演示:
CREATE TABLE dbo.HierarchyExample (
Id INT PRIMARY KEY,
Hieararchy HIERARCHYID NOT NULL
);
INSERT INTO dbo.HierarchyExample(Id, Hieararchy)
VALUES
(1, hierarchyid::Parse('/1/')),
(2, hierarchyid::Parse('/1/1/')),
(3, hierarchyid::Parse('/1/2/')),
(4, hierarchyid::Parse('/1/3/')),
(5, hierarchyid::Parse('/1/3/1/')),
(6, hierarchyid::Parse('/1/3/2/')),
(7, hierarchyid::Parse('/1/3/3/')),
(8, hierarchyid::Parse('/1/4/')),
(9, hierarchyid::Parse('/1/4/1/')),
(10, hierarchyid::Parse('/1/4/2/'));
CREATE INDEX IX_HierarchyExample_Hierarchy
ON dbo.HierarchyExample (Hieararchy);
SELECT descendant.*
FROM HierarchyExample ancestor
INNER JOIN HierarchyExample descendant
ON descendant.Hieararchy.IsDescendantOf(ancestor.Hieararchy) = 1
WHERE ancestor.Id = 1
DROP TABLE IF EXISTS dbo.HierarchyExample;
Run Code Online (Sandbox Code Playgroud)
descendant.Hieararchy.IsDescendantOf(ancestor.Hieararchy) = 1当然看起来它不应该是可控制的,但它似乎在这个特定案例的过程中很早就做了一些恶作剧。
如果我尝试
declare @x hierarchyid;
SELECT *
FROM HierarchyExample descendant
WHERE descendant.Hieararchy.IsDescendantOf(@x) = 1
OPTION (querytraceon 3604, querytraceon 8605, querytraceon 8606);
Run Code Online (Sandbox Code Playgroud)
由此产生的执行计划显示了对
Seek Keys[1]: Start: [tempdb].[dbo].[HierarchyExample].Hieararchy >= Scalar Operator([@x]),
End: [tempdb].[dbo].[HierarchyExample].Hieararchy <= Scalar Operator([@x].DescendantLimit())
Run Code Online (Sandbox Code Playgroud)
范围表达式已存在于转换后的树中。
转换后的树是来自解析器的解析树,导入并转换(“转换”)为早期编译阶段方便使用的树结构。
*** Converted Tree: ***
LogOp_Project QCOL: [descendant].Id QCOL: [descendant].Hieararchy
LogOp_Select
LogOp_Get TBL: HierarchyExample(alias TBL: descendant) HierarchyExample TableID=1093578934 TableReferenceID=0 IsRow: COL: IsBaseRow1000
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpLe
ScaOp_Identifier COL: @x
ScaOp_Identifier QCOL: [descendant].Hieararchy
ScaOp_Comp x_cmpLe
ScaOp_Identifier QCOL: [descendant].Hieararchy
ScaOp_UdtFunction EClrFunctionType_UdtMethodDescendantLimit IsDet NoDataAccess TI(hierarchyid,Null,Var,ML=892)
ScaOp_Identifier COL: @x
AncOp_PrjList
Run Code Online (Sandbox Code Playgroud)
所以我得出的结论是,在解析过程中,它会转换为潜在的可查找范围谓词。
在论文《对灵活模式场景的关系支持》中提到了这种转换,尽管它没有详细介绍。
为了支持所考虑的场景,我们定义了一个特定的操作,该操作在关系处理发生之前在内部重写。
H1列是 的后代的事实H2由 表示,H1.IsDescendant(H2)并且翻译后成为范围谓词:H2 >= H1 and H2 <= H1.DescendantLimit()。
此后优化就会正常进行。之后使用的查询转换规则只是SelIdxToRng-SelToTrivialFilter没有任何特定于 HierarchyId 的规则。
下面是更复杂的连接示例的转换后的树,显示的内容大致相同
*** Converted Tree: ***
LogOp_Project QCOL: [descendant].Id QCOL: [descendant].Hieararchy
LogOp_Select
LogOp_Join
LogOp_Get TBL: HierarchyExample(alias TBL: ancestor) HierarchyExample TableID=1093578934 TableReferenceID=0 IsRow: COL: IsBaseRow1000
LogOp_Get TBL: HierarchyExample(alias TBL: descendant) HierarchyExample TableID=1093578934 TableReferenceID=0 IsRow: COL: IsBaseRow1001
ScaOp_Logical x_lopAnd
ScaOp_Comp x_cmpLe
ScaOp_Identifier QCOL: [ancestor].Hieararchy
ScaOp_Identifier QCOL: [descendant].Hieararchy
ScaOp_Comp x_cmpLe
ScaOp_Identifier QCOL: [descendant].Hieararchy
ScaOp_UdtFunction EClrFunctionType_UdtMethodDescendantLimit IsDet NoDataAccess TI(hierarchyid,Null,Var,ML=892)
ScaOp_Identifier QCOL: [ancestor].Hieararchy
ScaOp_Comp x_cmpEq
ScaOp_Identifier QCOL: [ancestor].Id
ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=1)
AncOp_PrjList
*******************
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
407 次 |
| 最近记录: |