【问题标题】:T-SQL XPath query to join an Attribute from an ancestor to a child elementT-SQL XPath 查询将属性从祖先元素连接到子元素
【发布时间】:2017-08-21 01:27:08
【问题描述】:

我有以下 XML 文档片段:

<document id="856" version="004010" direction="outbound">
  <xpaths>
    <xpath id="deliverAddressCode">//ib//Transaction[1]/Loop[@LoopId="N1"]/N1[N101="ST"][N103="ZZ"]/N104/text()</xpath>
  </xpaths>
  <schema>
    <BSN BSN01="00" BSN02="{xpath::concat(substring(asn[1]/asnH[1]/senderId[1],1,5),asn[1]/asnH[1]/dino[1])}" BSN03="{xpath::.//asnH/date/text()}" BSN04="{xpath::.//asnH/time/text()}" />
    <DTM DTM01="011" DTM02="{xpath::.//asnH/shipDate/text()}" />
    <HL HL01="1" HL02="" HL03="S">
      <TD1 TD101="PLT" TD102="{xpath::.//asnH/loadingQty/text()}" TD103="" TD104="" TD105="" TD106="N" TD107="{xpath::.//asnH/zWeight/text()}" TD108="KG" />
      <TD5 TD501="" TD502="" TD503="" TD504="M" TD505="{sql::select [dbo].[fsLocalToRemoteMapping]('{xpath::.//asnH/baanId/text()}','{xpath::.//asnH/ISAusageIndicator}','forwardingAgents','{xpath::.//asnH/cfrw/text()}')}" />
      <TD3 TD301="TL" TD302="" TD303="{xpath::.//asnH/trackingNo/text()}" />
      <PER PER01="IC" PER02="?" PER03="EM" PER04="?@?.com" />
      <N1 N101="SF" N102="{xpath:://sf/nama/text()}" N103="ZZ" N104="{xpath:://sf/cuno/text()}">
        <N3_SF N301="{xpath:://sf/namc/text()}" />
        <N4_SF N401="{xpath:://sf/cityName/text()}" N402="{xpath:://sf/prov/text()}" N403="{xpath:://sf/pstc/text()}" N404="{xpath:://sf/country/text()}" />
      </N1>
      <N1 N101="ST" N102="{xpath:://ib//Transaction[1]/Loop[@LoopId='N1']/N1[N101='ST'][N103='ZZ']/N102/text()}" N103="ZZ" N104="{xpath:://ib//Transaction[1]/Loop[@LoopId='N1']/N1[N101='ST'][N103='ZZ']/N104/text()}" />
    </HL>
  </schema>
  <scripts>
    <function id="fCDEC">   function fCDEC(x) { if(x  &gt;= 3500.00) return "002" ; else return "002"; }</function>
  </scripts>
</document>

还有这个(上面的片段):

<schema>
  <BSN BSN01="00" BSN02="{xpath::concat(substring(asn[1]/asnH[1]/senderId[1],1,5),asn[1]/asnH[1]/dino[1])}" BSN03="{xpath::.//asnH/date/text()}" BSN04="{xpath::.//asnH/time/text()}" />
  <DTM DTM01="011" DTM02="{xpath::.//asnH/shipDate/text()}" />
  <HL HL01="1" HL02="" HL03="S">
    <TD1 TD101="PLT" TD102="{xpath::.//asnH/loadingQty/text()}" TD103="" TD104="" TD105="" TD106="N" TD107="{xpath::.//asnH/zWeight/text()}" TD108="KG" />
    <TD5 TD501="" TD502="" TD503="" TD504="M" TD505="{sql::select [dbo].[fsLocalToRemoteMapping]('{xpath::.//asnH/baanId/text()}','{xpath::.//asnH/ISAusageIndicator}','forwardingAgents','{xpath::.//asnH/cfrw/text()}')}" />
    <TD3 TD301="TL" TD302="" TD303="{xpath::.//asnH/trackingNo/text()}" />
    <PER PER01="IC" PER02="? EDI Services" PER03="EM" PER04="?@?.com" />
    <N1 N101="SF" N102="{xpath:://sf/nama/text()}" N103="ZZ" N104="{xpath:://sf/cuno/text()}">
      <N3_SF N301="{xpath:://sf/namc/text()}" />
      <N4_SF N401="{xpath:://sf/cityName/text()}" N402="{xpath:://sf/prov/text()}" N403="{xpath:://sf/pstc/text()}" N404="{xpath:://sf/country/text()}" />
    </N1>
    <N1 N101="ST" N102="{xpath:://ib//Transaction[1]/Loop[@LoopId='N1']/N1[N101='ST'][N103='ZZ']/N102/text()}" N103="ZZ" N104="{xpath:://ib//Transaction[1]/Loop[@LoopId='N1']/N1[N101='ST'][N103='ZZ']/N104/text()}" />
  </HL>
</schema>

如何从第一个片段检索到第二个片段的文档 id,所以查询结果是:

<schema id="856">
  <BSN BSN01="00" BSN02="{xpath::concat(substring(asn[1]/asnH[1]/senderId[1],1,5),asn[1]/asnH[1]/dino[1])}" BSN03="{xpath::.//asnH/date/text()}" BSN04="{xpath::.//asnH/time/text()}" />
  <DTM DTM01="011" DTM02="{xpath::.//asnH/shipDate/text()}" />
  <HL HL01="1" HL02="" HL03="S">
    <TD1 TD101="PLT" TD102="{xpath::.//asnH/loadingQty/text()}" TD103="" TD104="" TD105="" TD106="N" TD107="{xpath::.//asnH/zWeight/text()}" TD108="KG" />
    <TD5 TD501="" TD502="" TD503="" TD504="M" TD505="{sql::select [dbo].[fsLocalToRemoteMapping]('{xpath::.//asnH/baanId/text()}','{xpath::.//asnH/ISAusageIndicator}','forwardingAgents','{xpath::.//asnH/cfrw/text()}')}" />
    <TD3 TD301="TL" TD302="" TD303="{xpath::.//asnH/trackingNo/text()}" />
    <PER PER01="IC" PER02="? EDI Services" PER03="EM" PER04="?@?.com" />
    <N1 N101="SF" N102="{xpath:://sf/nama/text()}" N103="ZZ" N104="{xpath:://sf/cuno/text()}">
      <N3_SF N301="{xpath:://sf/namc/text()}" />
      <N4_SF N401="{xpath:://sf/cityName/text()}" N402="{xpath:://sf/prov/text()}" N403="{xpath:://sf/pstc/text()}" N404="{xpath:://sf/country/text()}" />
    </N1>
    <N1 N101="ST" N102="{xpath:://ib//Transaction[1]/Loop[@LoopId='N1']/N1[N101='ST'][N103='ZZ']/N102/text()}" N103="ZZ" N104="{xpath:://ib//Transaction[1]/Loop[@LoopId='N1']/N1[N101='ST'][N103='ZZ']/N104/text()}" />
  </HL>
</schema>

使用的查询

    --ALTER function [dbo].[fxDocSchema](@env varchar(10),@baanId varchar(10),@docId varchar(10)) returns xml as
    begin
        declare @baanId varchar(10)='010-000164',@env varchar(10)='test',@docId varchar(10)='856'
    --return(

    select t.c.query('.')
                 ,t.c.query('schema')
    from (select meta from EdiMeta where baanId=@baanId and env=Case Lower(@env)                                                                                                                                                 when 't' then 'test'
                                                                                                                                                 when 'p' then 'production'
                                                                                                                                                 else @env
                                                                                                                                     end) f(x)
    cross applyx.nodes('meta/partners/partner/documents/document[@id=sql:variable("@docId")]') t(c) 
    --)

end 

【问题讨论】:

    标签: sql-server sql-server-2008 tsql xpath xquery


    【解决方案1】:

    这样的?

    DECLARE @XML XML = N'<document id="856" version="004010" direction="outbound">
      <xpaths>
        <xpath id="deliverAddressCode">//ib//Transaction[1]/Loop[@LoopId="N1"]/N1[N101="ST"][N103="ZZ"]/N104/text()</xpath>
      </xpaths>
      <schema>
        <BSN BSN01="00" BSN02="{xpath::concat(substring(asn[1]/asnH[1]/senderId[1],1,5),asn[1]/asnH[1]/dino[1])}" BSN03="{xpath::.//asnH/date/text()}" BSN04="{xpath::.//asnH/time/text()}" />
        <DTM DTM01="011" DTM02="{xpath::.//asnH/shipDate/text()}" />
        <HL HL01="1" HL02="" HL03="S">
          <TD1 TD101="PLT" TD102="{xpath::.//asnH/loadingQty/text()}" TD103="" TD104="" TD105="" TD106="N" TD107="{xpath::.//asnH/zWeight/text()}" TD108="KG" />
          <TD5 TD501="" TD502="" TD503="" TD504="M" TD505="{sql::select [dbo].[fsLocalToRemoteMapping](''{xpath::.//asnH/baanId/text()}'',''{xpath::.//asnH/ISAusageIndicator}'',''forwardingAgents'',''{xpath::.//asnH/cfrw/text()}'')}" />
          <TD3 TD301="TL" TD302="" TD303="{xpath::.//asnH/trackingNo/text()}" />
          <PER PER01="IC" PER02="?" PER03="EM" PER04="?@?.com" />
          <N1 N101="SF" N102="{xpath:://sf/nama/text()}" N103="ZZ" N104="{xpath:://sf/cuno/text()}">
            <N3_SF N301="{xpath:://sf/namc/text()}" />
            <N4_SF N401="{xpath:://sf/cityName/text()}" N402="{xpath:://sf/prov/text()}" N403="{xpath:://sf/pstc/text()}" N404="{xpath:://sf/country/text()}" />
          </N1>
          <N1 N101="ST" N102="{xpath:://ib//Transaction[1]/Loop[@LoopId=''N1'']/N1[N101=''ST''][N103=''ZZ'']/N102/text()}" N103="ZZ" N104="{xpath:://ib//Transaction[1]/Loop[@LoopId=''N1'']/N1[N101=''ST''][N103=''ZZ'']/N104/text()}" />
        </HL>
      </schema>
      <scripts>
        <function id="fCDEC">   function fCDEC(x) { if(x  &gt;= 3500.00) return "002" ; else return "002"; }</function>
      </scripts>
    </document>'
    
    SELECT 
        x.value('@id', 'int') AS "@Id"
    ,   (
        Select 
            y.query('.') AS "*"
        FROM @XMl.nodes('document/schema/*') AS a(y)
        FOR XML PATH(''), TYPE
        )
    FROM @Xml.nodes('document') AS a(x)
    FOR XML PATH('schema')
    

    五项原则:

    1. 我将根中我想要的主级别隔离到主查询(外部 a(x),它是“文档”的根)
    2. 我隔离了子项(a(y)) 并对文档/模式/* 的所有子项进行查询。因此,如果您自己运行内部,您会看到三行上的所有子节点。
    3. 我不关心它们,所以我只使用查询来获取它们的所有部分并将它们标记为文本“*”。
    4. 我在嵌套选择中指定这是一种 XML,但不想在其上再赋予它一个层次,只保留它自己。 (对于 XML 路径(''),类型)
    5. 然后我再次设置主 ID 的属性,然后执行另一个 FOR XML 表达式以获取路径但忽略根设置(For XML Path('schema'))

    【讨论】:

      【解决方案2】:

      使用insert XML DML

      DECLARE @tt TABLE(x XML);
      INSERT INTO @tt(x)VALUES(N'<document id="856" version="004010" direction="outbound">
        <xpaths>
          <xpath id="deliverAddressCode">//ib//Transaction[1]/Loop[@LoopId="N1"]/N1[N101="ST"][N103="ZZ"]/N104/text()</xpath>
        </xpaths>
        <schema>
          <BSN BSN01="00" BSN02="{xpath::concat(substring(asn[1]/asnH[1]/senderId[1],1,5),asn[1]/asnH[1]/dino[1])}" BSN03="{xpath::.//asnH/date/text()}" BSN04="{xpath::.//asnH/time/text()}" />
          <DTM DTM01="011" DTM02="{xpath::.//asnH/shipDate/text()}" />
          <HL HL01="1" HL02="" HL03="S">
            <TD1 TD101="PLT" TD102="{xpath::.//asnH/loadingQty/text()}" TD103="" TD104="" TD105="" TD106="N" TD107="{xpath::.//asnH/zWeight/text()}" TD108="KG" />
            <TD5 TD501="" TD502="" TD503="" TD504="M" TD505="{sql::select [dbo].[fsLocalToRemoteMapping](''{xpath::.//asnH/baanId/text()}'',''{xpath::.//asnH/ISAusageIndicator}'',''forwardingAgents'',''{xpath::.//asnH/cfrw/text()}'')}" />
            <TD3 TD301="TL" TD302="" TD303="{xpath::.//asnH/trackingNo/text()}" />
            <PER PER01="IC" PER02="?" PER03="EM" PER04="?@?.com" />
            <N1 N101="SF" N102="{xpath:://sf/nama/text()}" N103="ZZ" N104="{xpath:://sf/cuno/text()}">
              <N3_SF N301="{xpath:://sf/namc/text()}" />
              <N4_SF N401="{xpath:://sf/cityName/text()}" N402="{xpath:://sf/prov/text()}" N403="{xpath:://sf/pstc/text()}" N404="{xpath:://sf/country/text()}" />
            </N1>
            <N1 N101="ST" N102="{xpath:://ib//Transaction[1]/Loop[@LoopId=''N1'']/N1[N101=''ST''][N103=''ZZ'']/N102/text()}" N103="ZZ" N104="{xpath:://ib//Transaction[1]/Loop[@LoopId=''N1'']/N1[N101=''ST''][N103=''ZZ'']/N104/text()}" />
          </HL>
        </schema>
        <scripts>
          <function id="fCDEC">   function fCDEC(x) { if(x  &gt;= 3500.00) return "002" ; else return "002"; }</function>
        </scripts>
      </document>');
      
      DECLARE @doc_id INT=856;
      
      SELECT
          s=x.n.query('.')
      INTO
          #x
      FROM
          @tt AS tt
          CROSS APPLY tt.x.nodes('/document[@id=sql:variable("@doc_id")]/schema') AS x(n);
      
      UPDATE
          #x
      SET
          s.modify('insert attribute id {sql:variable("@doc_id")} into (/schema[1])')
      
      SELECT * FROM #x;
      
      DROP TABLE #x;
      

      【讨论】:

      • 没有更优雅的方式吗..这似乎有点过头了
      • @TonyP Elegant 是一种主观资格,所以我无法回答。在任何情况下,如果您想使用 XML insert 更改架构元素,您首先需要将其选择为临时表(或表变量)。这是因为 modify 函数不允许在即席 SQL 查询(即时 IOW)中更改 XML。这就是为什么首先将模式节点选择到临时表中,然后在临时表中更改 XML,最后从临时表中选择结果。
      猜你喜欢
      • 1970-01-01
      • 2018-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-25
      • 1970-01-01
      • 2016-12-08
      相关资源
      最近更新 更多