从大文件中获取 XML 个节点的不同列表
Get a distinct list of XML nodes from large file
我正在尝试从 XML 文件中获取不同的节点名称列表。我使用类似于 的递归 CTE 取得了成功,但仅限于我的文件小于 100 万字符。除此之外,查询似乎永远不会 return。我的一些文件在 100m 字符附近。
我已经转而尝试使用 PowerShell。对于这个样本 XML:
<?xml version="1.0" encoding="UTF_8"?>
<root>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>1111111111</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>0</descendant2>
</childA>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>2</descendant2>
</childA>
<childB>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant3>0</descendant3>
</childB>
<childC>
<descendant4>0</descendant4>
</childC>
<childC>
<descendant4>6</descendant4>
</childC>
</root>
我已经达到:
$xml.childnodes[1].childnodes | select -uniq | foreach { $xml.childnodes[1].($_.name).childnodes.name | select -uniq }
这给了我:
descendant1
descendant2
descendant1
descendant3
descendant4
但这不包括更多的后代。最终,我试图将 table 恢复为 SQL,如下所示:
root | childA | descendant1
root | childA | descendant1_1
root | childA | descendant1_1_1
root | childA | descendant2
root | childB | descendant1
root | childB | descendant1_1
root | childB | descendant1_1_1
root | childB | descendant3
root | childC | descendant4
下面是一个简单的解决方案,假设您知道 xml 的深度。
但也许你可以用 xqury 完成整个事情,因此在 SQL 端
[xml]$x = "your xml here"
# ------ LEVEL 2 children
$L2 = $x | Select-Xml "//root/*/*"
foreach($n in $L2) {
$L1 = $n.node.ParentNode.LocalName
$CHILD = $n.node.localname
[PSCustomObject]@{L1=$L1; CHILD = $CHILD}
}
# ------ LEVEL 3 children
$L3 = $x | Select-Xml "//root/*/*/*"
foreach($n in $L3) {
$L1 = $n.node.ParentNode.ParentNode.LocalName
$CHILD = $n.node.localname
[PSCustomObject]@{L1=$L1; CHILD = $CHILD}
}
# ------ LEVEL 4 children
$L4 = $x | Select-Xml "//root/*/*/*/*"
foreach($n in $L4) {
$L1 = $n.node.ParentNode.ParentNode.ParentNode.LocalName
$CHILD = $n.node.localname
[PSCustomObject]@{L1=$L1; CHILD = $CHILD}
}
同时添加 sql xquery 版本。它仍然需要结构知识并且当时只做一个级别,但它没有交叉 apply/joins 所以可能会在大文件上更好地工作
select
T.c.query('local-name(.)') as self
,T.c.query('local-name(..)') as parent
,T.c.query('local-name(../..)') as Gparent
,T.c.query('local-name(../../..)') as GGparent
from @x.nodes('/root/*/*/*/*') T(c)
如果我们谈论的是这个问题的硬编码解决方案,那么这里是我使用 SQL 服务器的解决方案。
DECLARE @x XML = '
<root>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>1111111111</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>0</descendant2>
</childA>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>2</descendant2>
</childA>
<childB>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant3>0</descendant3>
</childB>
<childC>
<descendant4>0</descendant4>
</childC>
<childC>
<descendant4>6</descendant4>
</childC>
</root>
';
SELECT
x.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') root,
L2.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L2,
L3.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L3,
L4.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L4,
L5.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L5
FROM @x.nodes('/*') x(n)
OUTER APPLY x.n.nodes('*') L2(n)
OUTER APPLY L2.n.nodes('*') L3(n)
OUTER APPLY L3.n.nodes('*') L4(n)
OUTER APPLY L4.n.nodes('*') L5(n);
输出
+------+--------+-------------+---------------+-----------------+
| root | L2 | L3 | L4 | L5 |
+------+--------+-------------+---------------+-----------------+
| root | childA | descendant1 | descendant1_1 | descendant1_1_1 |
| root | childA | descendant2 | | |
| root | childA | descendant1 | descendant1_1 | descendant1_1_1 |
| root | childA | descendant2 | | |
| root | childB | descendant1 | descendant1_1 | descendant1_1_1 |
| root | childB | descendant3 | | |
| root | childC | descendant4 | | |
| root | childC | descendant4 | | |
+------+--------+-------------+---------------+-----------------+
我正在尝试从 XML 文件中获取不同的节点名称列表。我使用类似于 的递归 CTE 取得了成功,但仅限于我的文件小于 100 万字符。除此之外,查询似乎永远不会 return。我的一些文件在 100m 字符附近。
我已经转而尝试使用 PowerShell。对于这个样本 XML:
<?xml version="1.0" encoding="UTF_8"?>
<root>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>1111111111</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>0</descendant2>
</childA>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>2</descendant2>
</childA>
<childB>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant3>0</descendant3>
</childB>
<childC>
<descendant4>0</descendant4>
</childC>
<childC>
<descendant4>6</descendant4>
</childC>
</root>
我已经达到:
$xml.childnodes[1].childnodes | select -uniq | foreach { $xml.childnodes[1].($_.name).childnodes.name | select -uniq }
这给了我:
descendant1
descendant2
descendant1
descendant3
descendant4
但这不包括更多的后代。最终,我试图将 table 恢复为 SQL,如下所示:
root | childA | descendant1
root | childA | descendant1_1
root | childA | descendant1_1_1
root | childA | descendant2
root | childB | descendant1
root | childB | descendant1_1
root | childB | descendant1_1_1
root | childB | descendant3
root | childC | descendant4
下面是一个简单的解决方案,假设您知道 xml 的深度。 但也许你可以用 xqury 完成整个事情,因此在 SQL 端
[xml]$x = "your xml here"
# ------ LEVEL 2 children
$L2 = $x | Select-Xml "//root/*/*"
foreach($n in $L2) {
$L1 = $n.node.ParentNode.LocalName
$CHILD = $n.node.localname
[PSCustomObject]@{L1=$L1; CHILD = $CHILD}
}
# ------ LEVEL 3 children
$L3 = $x | Select-Xml "//root/*/*/*"
foreach($n in $L3) {
$L1 = $n.node.ParentNode.ParentNode.LocalName
$CHILD = $n.node.localname
[PSCustomObject]@{L1=$L1; CHILD = $CHILD}
}
# ------ LEVEL 4 children
$L4 = $x | Select-Xml "//root/*/*/*/*"
foreach($n in $L4) {
$L1 = $n.node.ParentNode.ParentNode.ParentNode.LocalName
$CHILD = $n.node.localname
[PSCustomObject]@{L1=$L1; CHILD = $CHILD}
}
同时添加 sql xquery 版本。它仍然需要结构知识并且当时只做一个级别,但它没有交叉 apply/joins 所以可能会在大文件上更好地工作
select
T.c.query('local-name(.)') as self
,T.c.query('local-name(..)') as parent
,T.c.query('local-name(../..)') as Gparent
,T.c.query('local-name(../../..)') as GGparent
from @x.nodes('/root/*/*/*/*') T(c)
如果我们谈论的是这个问题的硬编码解决方案,那么这里是我使用 SQL 服务器的解决方案。
DECLARE @x XML = '
<root>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>1111111111</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>0</descendant2>
</childA>
<childA>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant2>2</descendant2>
</childA>
<childB>
<descendant1>
<descendant1_1>
<descendant1_1_1>2222222222</descendant1_1_1>
</descendant1_1>
</descendant1>
<descendant3>0</descendant3>
</childB>
<childC>
<descendant4>0</descendant4>
</childC>
<childC>
<descendant4>6</descendant4>
</childC>
</root>
';
SELECT
x.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') root,
L2.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L2,
L3.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L3,
L4.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L4,
L5.n.value('fn:local-name(.)', 'NVARCHAR(MAX)') L5
FROM @x.nodes('/*') x(n)
OUTER APPLY x.n.nodes('*') L2(n)
OUTER APPLY L2.n.nodes('*') L3(n)
OUTER APPLY L3.n.nodes('*') L4(n)
OUTER APPLY L4.n.nodes('*') L5(n);
输出
+------+--------+-------------+---------------+-----------------+
| root | L2 | L3 | L4 | L5 |
+------+--------+-------------+---------------+-----------------+
| root | childA | descendant1 | descendant1_1 | descendant1_1_1 |
| root | childA | descendant2 | | |
| root | childA | descendant1 | descendant1_1 | descendant1_1_1 |
| root | childA | descendant2 | | |
| root | childB | descendant1 | descendant1_1 | descendant1_1_1 |
| root | childB | descendant3 | | |
| root | childC | descendant4 | | |
| root | childC | descendant4 | | |
+------+--------+-------------+---------------+-----------------+