英文:
Extract execution plan in XML format to table
问题
我正在尝试以通用的方式从SQL服务器执行计划中提取数据。
例如,对于以下查询的执行计划:
SELECT *
FROM sys.all_objects o1
在SSMS中显示如下:
用户界面显示了节点以及每个节点的成本和百分比。我如何将这些信息从底层XML提取到表结构中?
我已经尝试自己查询XML,但似乎XML结构在不同查询之间都在变化。
英文:
I'm trying to extract the data from SQL server execution plans in a generic way.
As an example the execution plan for
SELECT *
FROM sys.all_objects o1
as shown in SSMS is below
The UI shows nodes along with costs for each node and percentages. How can I extract this from the underlying XML into a table structure?
I've tried to query the XML by my self, but it seems that the XML structure is changing from query to query.
答案1
得分: 1
这应该可以帮助您入门(DB Fiddle示例)。
DECLARE @X XML = N'<?xml version="1.0" encoding="utf-16"?><ShowPlanXML ...';
DECLARE @Nodes TABLE
(
PlanId INT,
NodeId INT,
PhysicalOp VARCHAR(200),
EstimatedTotalSubtreeCost FLOAT,
EstimatedOperatorCost FLOAT,
ParentNodeId INT NULL,
PRIMARY KEY(PlanId, NodeId)
);
WITH XMLNAMESPACES (default 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
plans AS
(
SELECT ROW_NUMBER() over (order by qp) as PlanId, qp.query('.') as plan_xml
FROM @X.nodes('//QueryPlan') n(qp)
)
INSERT @Nodes(PlanId, NodeId, PhysicalOp, EstimatedTotalSubtreeCost, ParentNodeId)
SELECT PlanId,
NodeId = relop.value('@NodeId', 'int'),
PhysicalOp = relop.value('@PhysicalOp', 'varchar(200)'),
EstimatedTotalSubtreeCost = relop.value('@EstimatedTotalSubtreeCost', 'float'),
/*XPath ancestor axis not supported so just go up a few levels and look for the closest ancestor Relop*/
ParentNodeId = COALESCE(
relop.value('..[local-name() = "RelOp"]/@NodeId', 'int'),
relop.value('../..[local-name() = "RelOp"]/@NodeId', 'int'),
relop.value('../../..[local-name() = "RelOp"]/@NodeId', 'int'),
relop.value('../../../..[local-name() = "RelOp"]/@NodeId', 'int')
)
FROM plans
CROSS APPLY plan_xml.nodes('//RelOp') n(relop);
UPDATE N1
SET EstimatedOperatorCost = EstimatedTotalSubtreeCost - ISNULL((SELECT SUM(EstimatedTotalSubtreeCost) FROM @Nodes N2 WHERE N1.PlanId = N2.PlanId AND N2.ParentNodeId = N1.NodeId),0)
FROM @Nodes N1
SELECT *,
EstPctOperatorCost = FORMAT(EstimatedOperatorCost/MAX(EstimatedTotalSubtreeCost) OVER (PARTITION BY PlanId), 'P0')
FROM @Nodes
执行计划是一棵树 - 有可能有更加优雅的方式来获取父运算符,不过上述是我的尝试!
上述代码尚未在超过两个执行计划的样本大小上经过充分测试,因此您可能会遇到需要修复的问题。
您可以访问URI http://schemas.microsoft.com/sqlserver/2004/07/showplan 以查看有关各种模式的信息,尽管由于某种原因,除非我使用隐身模式,否则一直会显示"请求被阻止"。
[1]: https://dbfiddle.uk/hq66aIte
英文:
This should get you started (DB Fiddle example).
DECLARE @X XML = N'<?xml version="1.0" encoding="utf-16"?><ShowPlanXML ...';
DECLARE @Nodes TABLE
(
PlanId INT,
NodeId INT,
PhysicalOp VARCHAR(200),
EstimatedTotalSubtreeCost FLOAT,
EstimatedOperatorCost FLOAT,
ParentNodeId INT NULL,
PRIMARY KEY(PlanId, NodeId)
);
WITH XMLNAMESPACES (default 'http://schemas.microsoft.com/sqlserver/2004/07/showplan'),
plans AS
(
SELECT ROW_NUMBER() over (order by qp) as PlanId, qp.query('.') as plan_xml
FROM @X.nodes('//QueryPlan') n(qp)
)
INSERT @Nodes(PlanId, NodeId, PhysicalOp, EstimatedTotalSubtreeCost, ParentNodeId)
SELECT PlanId,
NodeId = relop.value('@NodeId', 'int'),
PhysicalOp = relop.value('@PhysicalOp', 'varchar(200)'),
EstimatedTotalSubtreeCost = relop.value('@EstimatedTotalSubtreeCost', 'float'),
/*XPath ancestor axis not supported so just go up a few levels and look for the closest ancestor Relop*/
ParentNodeId = COALESCE(
relop.value('..[local-name() = "RelOp"]/@NodeId', 'int'),
relop.value('../..[local-name() = "RelOp"]/@NodeId', 'int'),
relop.value('../../..[local-name() = "RelOp"]/@NodeId', 'int'),
relop.value('../../../..[local-name() = "RelOp"]/@NodeId', 'int')
)
FROM plans
CROSS APPLY plan_xml.nodes('//RelOp') n(relop);
UPDATE N1
SET EstimatedOperatorCost = EstimatedTotalSubtreeCost - ISNULL((SELECT SUM(EstimatedTotalSubtreeCost) FROM @Nodes N2 WHERE N1.PlanId = N2.PlanId AND N2.ParentNodeId = N1.NodeId),0)
FROM @Nodes N1
SELECT *,
EstPctOperatorCost = FORMAT(EstimatedOperatorCost/MAX(EstimatedTotalSubtreeCost) OVER (PARTITION BY PlanId), 'P0')
FROM @Nodes
The execution plan is a tree - there are likely more elegant ways of getting the parent operator than my attempt!
The above is not battle tested across a sample size of more than two execution plans so you may well encounter issues with it that you will need to fix.
You can visit the URI http://schemas.microsoft.com/sqlserver/2004/07/showplan to see information about the various schemas though for some reason I've never got to the bottom of it displays "The request is blocked." for me unless I use incognito mode.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论