【问题标题】:Apply previously shredded xml values to subsequent elements将先前粉碎的 xml 值应用于后续元素
【发布时间】:2019-06-26 16:15:20
【问题描述】:

我有以下 xml。如何在遇到新的 cond-id 而不创建重复行之前保留每个 cond-id 的值并将其应用于每个后续的价格容器元素。

我已经尝试了几件事,这是我最接近的。

declare @xml xml =
'-<module mod-id="333">
<title>NNN NNNNN NNNNN</title>
-<data-code>
<code-id>333-004</code-id>
<description>XXX XXX  XXXXXXXX XXX X  XXXXXXXXXXXXXXXX</description>
-<applic>
-<p32>

-<condition cond-id="o-000008888">
<ctext> NNN NNNNNNNNN NN NNNNN NNNNNNNNN NN</ctext>
</condition>
-<m234 domicile="all" ver-start="4.40" target-ver="4.40" vocation="all">
-<value>
<price-container pwdb-id="p-000121212">267</price-container>
<weight-container pwdb-id="w-000676767">0/40</weight-container>
</value>
</m234>
</p32>
</applic>
</data-code>

-<data-code>
<code-id>333-005</code-id>
<description>NNNNNNNNNNNNN NNNNNNNNNN NNNNN N NNNN N NNN NNNNNNNN</description>
-<applic>
-<p32>

-<condition cond-id="o-000002222">
<ctext> NNNNNN NNNNN XXXX NNNN XXXX NNNNNNNN XXXXXXXX </ctext>
</condition>
-<m234 domicile="all" ver-start="4.40" target-ver="4.40" vocation="all">
-<value>
<price-container pwdb-id="p-000123456">N/C</price-container>
<weight-container pwdb-id="w-000234567">0/0</weight-container>
</value>
</m234>

-<condition cond-id="o-000033333">
<ctext> Price with DC 622-005 , DC 622-197, , and DC 622-292 </ctext>
</condition>
-<m234 domicile="all" ver-start="4.80" target-ver="4.80" vocation="all">
-<value>
<price-container pwdb-id="p-000456789">99999</price-container>
<weight-container pwdb-id="w-000789012">0/0</weight-container>
</value>
</m234>

</p32>
</applic>
</data-code>
</module>'


select 
price.value('(../../../../../code-id)[1]', 'varchar(50)') as data_code,
price.value('@pwdb-id', 'varchar(50)') as pwdb_id,
price.value('(text())[1]', 'varchar(50)') as text,
cond.value('(@cond-id) [1]', 'varchar(50)') as cond_id
from @xml.nodes('/module/data-code/applic/p32') as Xtble(datanode)
cross apply Xtble.datanode.nodes('m234/value/price-container') as Xtble2(price)
outer apply Xtble.datanode.nodes('condition') as Xtble3(cond)

这就是我得到的

data_code   pwdb_id         text    cond_id
333-004     p-000121212     267     o-000008888
333-005     p-000123456     N/C     o-000002222
333-005     p-000456789     99999   o-000002222
333-005     p-000123456     N/C     o-000033333
333-005     p-000456789     99999   o-000033333

这就是我想要的

data_code   pwdb_id         text    cond_id
333-004     p-000121212     267     o-000008888
333-005     p-000123456     N/C     o-000002222
333-005     p-000456789     99999   o-000033333

【问题讨论】:

  • 您的 XML 中真的有那些减号,还是只是从 Internet Explorer 复制/粘贴引入的工件?

标签: sql-server xml tsql


【解决方案1】:

在这种情况下,我更喜欢使用 CROSS APPLY 来计算 tally-on-the-fly 作为派生集,并使用 sql:column() 来读取 position-related节点通过位置谓词

declare @xml xml =
N'<module mod-id="333">
  <title>NNN NNNNN NNNNN</title>
  <data-code>
    <code-id>333-004</code-id>
    <description>XXX XXX  XXXXXXXX XXX X  XXXXXXXXXXXXXXXX</description>
    <applic>
      <p32>
        <condition cond-id="o-000008888">
          <ctext> NNN NNNNNNNNN NN NNNNN NNNNNNNNN NN</ctext>
        </condition>
        <m234 domicile="all" ver-start="4.40" target-ver="4.40" vocation="all">
          <value>
            <price-container pwdb-id="p-000121212">267</price-container>
            <weight-container pwdb-id="w-000676767">0/40</weight-container>
          </value>
        </m234>
      </p32>
    </applic>
  </data-code>
  <data-code>
    <code-id>333-005</code-id>
    <description>NNNNNNNNNNNNN NNNNNNNNNN NNNNN N NNNN N NNN NNNNNNNN</description>
    <applic>
      <p32>
        <condition cond-id="o-000002222">
          <ctext> NNNNNN NNNNN XXXX NNNN XXXX NNNNNNNN XXXXXXXX </ctext>
        </condition>
        <m234 domicile="all" ver-start="4.40" target-ver="4.40" vocation="all">
          <value>
            <price-container pwdb-id="p-000123456">N/C</price-container>
            <weight-container pwdb-id="w-000234567">0/0</weight-container>
          </value>
        </m234>
        <condition cond-id="o-000033333">
          <ctext> Price with DC 622-005 , DC 622-197, , and DC 622-292 </ctext>
        </condition>
        <m234 domicile="all" ver-start="4.80" target-ver="4.80" vocation="all">
          <value>
            <price-container pwdb-id="p-000456789">99999</price-container>
            <weight-container pwdb-id="w-000789012">0/0</weight-container>
          </value>
        </m234>
      </p32>
    </applic>
  </data-code>
</module>';

--查询

SELECT A.dc.value('(code-id/text())[1]','nvarchar(100)') AS data_code
      ,A.dc.value('(applic/p32/m234[sql:column("Nmbr")]/value/price-container/@pwdb-id)[1]','nvarchar(100)') AS pwdb_id
      ,A.dc.value('(applic/p32/m234[sql:column("Nmbr")]/value/price-container/text())[1]','nvarchar(100)') AS [text]
      ,A.dc.value('(applic/p32/condition[sql:column("Nmbr")]/@cond-id)[1]','nvarchar(100)') AS cond_id
FROM @xml.nodes('/module/data-code') A(dc)
CROSS APPLY(SELECT TOP(A.dc.value('count(applic/p32/condition)','int')) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) FROM master..spt_values) B(Nmbr);

结果

data_code   pwdb_id     text    cond_id
333-004     p-000121212 267     o-000008888
333-005     p-000123456 N/C     o-000002222
333-005     p-000456789 99999   o-000033333

简而言之:

我们使用.nodes() 来获取重复的&lt;data-code&gt; 节点。
现在我们使用 CROSS APPLY 的技巧:这将返回一个从 1 到 n 的列表作为派生集,其中 n 是 &lt;condition&gt; 节点的计数。
如您所见,我可以使用sql:column("Nmbr") 中的数字(由APPLY 返回)。这将选择condition[1] 连同m234[1]condition[2] 连同m234[2] 等等。

向后导航../ 和建议的&lt;&lt; 的任何使用都会非常缓慢...

【讨论】:

  • 谢谢,效果很好!在分解 xml 方面我还有很多东西要学。
【解决方案2】:

您可以计算每个 CROSS APPLY 的行号并将它们关联起来,如下所示:

SELECT x1.data_code, x1.pwdb_id, x1.text, x2.cond_id
FROM @xml.nodes('/module/data-code/applic/p32') as Xtble(datanode)
CROSS APPLY (
    SELECT ROW_NUMBER() OVER (ORDER BY price) AS RowNum1, 
        price.value('(../../../../../code-id)[1]', 'varchar(50)') as data_code,
        price.value('@pwdb-id', 'varchar(50)') as pwdb_id,
        price.value('(text())[1]', 'varchar(50)') as text
    FROM Xtble.datanode.nodes('m234/value/price-container') as Xtble2(price)
) x1
CROSS APPLY (
    SELECT ROW_NUMBER() OVER (ORDER BY cond) AS RowNum2, 
        cond.value('(@cond-id) [1]', 'varchar(50)') as cond_id
    FROM Xtble.datanode.nodes('condition') as Xtble3(cond)
) x2
WHERE x1.RowNum1=x2.RowNum2

【讨论】:

    【解决方案3】:

    您可以使用&lt;&lt; 获取一组先前的节点,使用last() 获取最后一个节点。

    select D.X.value('(code-id/text())[1]', 'varchar(50)') as data_code,
           M.X.value('(value/price-container/@pwdb-id)[1]', 'varchar(50)') as pwdb_id,
           M.X.value('(value/price-container/text())[1]', 'varchar(50)') as text,
           M.X.value('(for $n in ../* where $n << . return $n)[last()]/@cond-id', 'varchar(50)')
    from @xml.nodes('/module/data-code') as D(X)
      cross apply D.X.nodes('applic/p32/m234') as M(X)
    

    【讨论】:

    • 在这种情况下,我更喜欢使用 tally-on-the-fly。我想——如果我没记错的话——我不久前从你的一篇文章中学到了诀窍。我对&lt;&lt; 的体验不是最好的(在性能方面)。
    • @Shnugo 是的,你是对的。
    猜你喜欢
    • 2020-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-12
    • 1970-01-01
    相关资源
    最近更新 更多