简体   繁体   English

SQL - XML - 在子查询中创建多个同名节点

[英]SQL - XML - Create multiple nodes with same name in subquery

I'm having an issue, when generating an XML file from SQL.从 SQL 生成 XML 文件时遇到问题。 Generally it works fine, but now the issue is, that I want to display mutliple nodes with the same name <cbc:Note> in a subquery.通常它工作正常,但现在的问题是,我想在子查询中显示具有相同名称<cbc:Note>多个节点。 I know I can force a split with a null column in between, but then it generates a new InvoiceLine我知道我可以强制拆分中间有一个null列,但是它会生成一个新的InvoiceLine

The original query has more than 1000 lines of code, so I will just post the data, which is needed.原来的查询有1000多行代码,所以我就贴数据,这是需要的。

The query starts this way:查询以这种方式开始:

WITH XMLNAMESPACES (
                        'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2' as ext,
                        'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' as cbc,
                        'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' as cac,
                        'http://uri.etsi.org/01903/v1.3.2#' as xades,
                        'http://www.w3.org/2001/XMLSchema-instance' as xsi,
                        'http://www.w3.org/2000/09/xmldsig#' as ds
                    )   

SELECT
    @XMLData = xmldat.xmldataCol 
FROM
(
    SELECT
        (

        SELECT
            -- HIER XML Daten generieren
            ''                                                              as 'ext:UBLExtensions',
            ''                                                              as 'ext:UBLExtensions/ext:UBLExtension',
            ''                                                              as 'ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent',
            '2.1'                                                           as 'cbc:UBLVersionID',
            'TR1.2'                                                         as 'cbc:CustomizationID',
            -- evtl. Kommentar für geschart = 1
            case 
            when v2.geschart != 1 then 'IHRACAT'
            else '###PROFILE###' end                                        as 'cbc:ProfileID',
            '###TRANSACTION_NO###'                                          as 'cbc:ID',
            'false'                                                         as 'cbc:CopyIndicator',
            ....

At some point in the code it generates multiple invoice lines.在代码中的某个点,它会生成多个发票行。 In an invoice line, I need to have multiple <cbc:Note> nodes.在发票行中,我需要有多个<cbc:Note>节点。 This is the code:这是代码:

        ...
        case when v2.RECINFW = 1 then
            cast(v2.kurs as decimal(18,4))              
        else
            null
        end                                         as 'cac:PricingExchangeRate/cbc:CalculationRate',
        /* 
            Invoice Lines
            | | | | | | | | | | | | | | | | |
            v v v v v v v v v v v v v v v v v
        */
        (
            SELECT 
                vp2.pos_nr                                                      as 'cac:InvoiceLine/cbc:ID',
                'PalWeight:' + cast(cast(isnull(vp2.gew_pal,'') as decimal(18,4)) as nvarchar(50))  as 'cbc:Note',
                'PackUnit:' + cast(cast(isnull(a.VERP_EIN,'') as decimal(18,4)) as nvarchar(50))            as 'cbc:Note',
                case when vp2.DRUCKFLG=4 then cast(isnull(vp2.BEMERK,'') as nvarchar(max)) else null end    as 'cbc:Note',
                ''                                                                                          as 'cbc:SomeOtherField',
                ...
            FROM 
                vorgpos2 vp2 (nolock)
            inner join
                artikel a (nolock) on vp2.ARTKENN = a._KENN
            WHERE 
                vp2.vorgang2 = v2._kenn FOR XML path (''), root('REPLACEINVOICELINEREPLACE'), type
        ),

When I put a null in between the cbc:Notes columns,当我在cbc:Notes列之间放置一个null时,

'PalWeight:' + cast(cast(isnull(vp2.gew_pal,'') as decimal(18,4)) as nvarchar(50))  as 'cbc:Note',
null,
'PackUnit:' + cast(cast(isnull(a.VERP_EIN,'') as decimal(18,4)) as nvarchar(50))            as 'cbc:Note',
null,
case when vp2.DRUCKFLG=4 then cast(isnull(vp2.BEMERK,'') as nvarchar(max)) else null end    as 'cbc:Note',

it breaks the complete invoice line (stop one and create a new one):它打破了完整的发票行(停止并创建一个新的):

<cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:Note>PalWeight:1000.0000</cbc:Note>
</cac:InvoiceLine>
<cac:InvoiceLine>
    <cbc:Note>PackUnit:25.0000</cbc:Note>
</cac:InvoiceLine>
<cac:InvoiceLine>
    <cbc:InvoicedQuantity unitCode="KGM">100.0000</cbc:InvoicedQuantity>
    ...

Wanted result:想要的结果:

<cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:Note>PalWeight:1000.0000</cbc:Note>
    <cbc:Note>PackUnit:25.0000</cbc:Note>
    <cbc:InvoicedQuantity unitCode="KGM">100.0000</cbc:InvoicedQuantity>
    ...

If you wonder about the missing REPLACEINVOICELINEREPLACE root element in the subquery.如果您想知道子查询中缺少REPLACEINVOICELINEREPLACE根元素。 This is being replaced afterwards.这个是后来换的。 Has anyone a hint or a solution for this problem?有没有人有这个问题的提示或解决方案? Many thanks in advance!提前谢谢了!

SQL XML is all kinds of weird. SQL XML 很奇怪。 You might want to try something like the following (note the null as 'x' columns):您可能想尝试以下操作(注意null as 'x'列):

WITH XMLNAMESPACES (
  'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2' as cbc,
  'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2' as cac
)
SELECT
  pos_nr                                                      as 'cbc:ID',
  'PalWeight:' + cast(cast(isnull(gew_pal,'') as decimal(18,4)) as nvarchar(50))  as 'cbc:Note',
  null as 'x',
  'PackUnit:' + cast(cast(isnull(VERP_EIN,'') as decimal(18,4)) as nvarchar(50))            as 'cbc:Note',
  null as 'x',
  case when DRUCKFLG=4 then cast(isnull(BEMERK,'') as nvarchar(max)) else null end    as 'cbc:Note'
FROM (values
  (1, 1000.0, 25.0, 4, 100.0)
) dat (pos_nr, gew_pal, verp_ein, druckflg, bemerk)
FOR XML path ('cac:InvoiceLine'), root('foo'), type

Which yields:其中产生:

<foo xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
  <cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:Note>PalWeight:1000.0000</cbc:Note>
    <cbc:Note>PackUnit:25.0000</cbc:Note>
    <cbc:Note>100.0</cbc:Note>
  </cac:InvoiceLine>
</foo>

Credit where credit is due: for xml path() concatenates texts, how to get 2 elements with the same name?信用到期的信用: 对于 xml path() 连接文本,如何获得 2 个具有相同名称的元素?

Note: pay attention to casting an empty string '' to decimal, you'll get an error注意:注意将空字符串 '' 转换为十进制,会报错

select 'PalWeight:' + cast(cast(isnull(null,'') as decimal(18,4)) as nvarchar(50)) 
go
--concat is more flexible with null
select concat('PalWeight:', null) 
go

The xml part: xml部分:

--edited this part, to include the attribute on a column (the attribute to a following element was apparently the culprit and i got confused as to how multi InvoiceLines could happen). 
--Just adjust the other xml generation options accordingly.

select
pos_nr as 'InvoiceLine/ID',
null as 'InvoiceLine',
gew_pal as 'InvoiceLine/Note',
null as 'InvoiceLine',
verp_ein as 'InvoiceLine/Note',
null as 'InvoiceLine',
DRUCKFLG as 'InvoiceLine/Note',
someothercol3 as 'InvoiceLine/someothercol1/@attributeofthefollowingcolumncol1', --<-- reason of confusion
someothercol1 as 'InvoiceLine/someothercol1',
someothercol2 as 'InvoiceLine/someothercol2' 
from 
(
values(1, 'a1', 'b1', 'c1', 'd1', 'e1', 'f1'),(2, 'a2', 'b2', null, 'd2', null, 'f2'),(3, null, 'b3', 'c3', 'd3', null, null)
) as t(pos_nr, gew_pal, verp_ein, DRUCKFLG, someothercol1, someothercol2, someothercol3)
for xml path('');


select
pos_nr as 'InvoiceLine/ID',
(select 
    gew_pal as 'Note',
    null,
    verp_ein as 'Note',
    null,
    DRUCKFLG as 'Note',
    someothercol1 as 'someothercol1',
    someothercol2 as 'someothercol2' 
for xml path(''), type
) as 'InvoiceLine/*'
from 
(
values(1, 'a1', 'b1', 'c1', 'd1', 'e1', 'f1'),(2, 'a2', 'b2', null, 'd2', null, 'f2'),(3, null, 'b3', 'c3', 'd3', null, null)
) as t(pos_nr, gew_pal, verp_ein, DRUCKFLG, someothercol1, someothercol2, someothercol3)
for xml path('');


select
pos_nr as 'ID',
(select 
    gew_pal as 'Note',
    null,
    verp_ein as 'Note',
    null,
    DRUCKFLG as 'Note',
    someothercol1 as 'someothercol1',
    someothercol2 as 'someothercol2' 
for xml path(''), type
) 
from 
(
values(1, 'a1', 'b1', 'c1', 'd1', 'e1', 'f1'),(2, 'a2', 'b2', null, 'd2', null, 'f2'),(3, null, 'b3', 'c3', 'd3', null, null)
) as t(pos_nr, gew_pal, verp_ein, DRUCKFLG, someothercol1, someothercol2, someothercol3)
for xml path('InvoiceLine');

You can either use another sub-select (if your data is set based, or an element closer in between:您可以使用另一个子选择(如果您的数据是基于设置的,或者介于两者之间的元素

        SELECT 
            vp2.pos_nr                                                      as 'cac:InvoiceLine/cbc:ID',
            'PalWeight:' + cast(cast(isnull(vp2.gew_pal,'') as decimal(18,4)) as nvarchar(50))  as 'cac:InvoiceLine/cbc:Note',
            NULL as'cac:InvoiceLine',
            'PackUnit:' + cast(cast(isnull(a.VERP_EIN,'') as decimal(18,4)) as nvarchar(50))            as 'cac:InvoiceLine/cbc:Note',
            NULL as'cac:InvoiceLine',
            case when vp2.DRUCKFLG=4 then cast(isnull(vp2.BEMERK,'') as nvarchar(max)) else null end    as 'cac:InvoiceLine/cbc:Note',
            ''                                                                                          as 'cac:InvoiceLine/cbc:SomeOtherField',
            ...
        FROM 

The idea in short:简而言之这个想法:

The engine opens the <cac:InvoiceLine> and then opens the <cbc:ID> element to insert the vb2.pos_nr as text() -node.引擎打开<cac:InvoiceLine> ,然后打开<cbc:ID>元素以插入vb2.pos_nr作为text() -node。
The next line tells the engine the path 'cac:InvoiceLine/cbc:Note' .下一行告诉引擎路径'cac:InvoiceLine/cbc:Note' The engine thinks: "Oh, nothing more for <cbc:ID> , let's close this element. Ah, we continue with <cac:InvoiceLine> , which is still open. Let's insert a <cbc:Note> " .引擎认为: “哦,没有更多<cbc:ID> ,让我们关闭这个元素。啊,我们继续<cac:InvoiceLine> ,它仍然是打开的。让我们插入一个<cbc:Note>
Now the magic happes: The eninge thinks: "Ok, we have to close <cbc:Node> , but we continue within <cac:InvoiceLine> . Oops, nothing to insert... Never mind, let's pick the next. Okay, we are still within <cac:InvoiceLine> but we have to open a <cbc:Node> element."现在奇迹发生了: eninge 想: “好吧,我们必须关闭<cbc:Node> ,但我们在<cac:InvoiceLine>继续。哎呀,没有什么可插入的......没关系,让我们选择下一个。好吧,我们仍在<cac:InvoiceLine>但我们必须打开一个<cbc:Node>元素。”
And so on...等等...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM