SQL XML - 从 SQL Server 为发票创建一个 XML 文件,包括一个 XML 文件中的发票位置

Posted

技术标签:

【中文标题】SQL XML - 从 SQL Server 为发票创建一个 XML 文件,包括一个 XML 文件中的发票位置【英文标题】:SQL XML - Create a XML file from SQL Server for an invoice including invoice positions in one XML file 【发布时间】:2020-10-27 13:13:02 【问题描述】:

我以这种方式创建我的xml文件(我没有显示所有输出字段,因为有很多字段):

DECLARE @ID_Rechnung int = 8;

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',
            ''                                          AS 'cbc:ProfileID',
            Rechnungen.Nummer                           AS 'cbc:ID',
            'false'                                     AS 'cbc:CopyIndicator',
            ''                                          AS 'cbc:UUID',
            CAST(Rechnungen.Datum AS Date)              AS 'cbc:IssueDate'      
            
        FROM 
            rechnungen  
        WHERE 
            rechnungen.id = @ID_Rechnung


        FOR XML PATH(''), ROOT('Invoice') 
    ) AS xmldataCol

这很好用 - 我得到以下 XML:

<Invoice xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
  <ext:UBLExtensions>
    <ext:UBLExtension>
      <ext:ExtensionContent />
    </ext:UBLExtension>
  </ext:UBLExtensions>
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:CustomizationID>TR1.2</cbc:CustomizationID>
  <cbc:ProfileID />
  <cbc:ID>R200001</cbc:ID>
  <cbc:CopyIndicator>false</cbc:CopyIndicator>
  <cbc:UUID />
  <cbc:IssueDate>2020-06-29</cbc:IssueDate>
</Invoice>

但现在我需要同一文件中的发票位置。

此 SQL 应包含在第一个 SQL 中,日期应为 xml 文件中的发票行:


SELECT
  Rechnungpos.ID                        AS 'cac:InvoiceLine/cbc:ID',
  Rechnungpos.Anzahl                    AS 'cac:InvoiceLine/cbc:InvoicedQuantity'
FROM
  RechnungPos 
WHERE
  RechnungPos.id_Rechnung = @ID_Rechnung

输出应该是这样的:

<Invoice xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
  <ext:UBLExtensions>
    <ext:UBLExtension>
      <ext:ExtensionContent />
    </ext:UBLExtension>
  </ext:UBLExtensions>
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:CustomizationID>TR1.2</cbc:CustomizationID>
  <cbc:ProfileID />
  <cbc:ID>R200001</cbc:ID>
  <cbc:CopyIndicator>false</cbc:CopyIndicator>
  <cbc:UUID />
  <cbc:IssueDate>2020-06-29</cbc:IssueDate>
  <cac:InvoiceLine>
    <cbc:ID>1<(cbc:>
    <cbc:InvoicedQuantity>3</cbc:InvoicedQuantity>
  </cac:InvoiceLine>
  <cac:InvoiceLine>
    <cbc:ID>5<(cbc:>
    <cbc:InvoicedQuantity>1</cbc:InvoicedQuantity>
  </cac:InvoiceLine>
  <cac:InvoiceLine>
    <cbc:ID>9<(cbc:>
    <cbc:InvoicedQuantity>2</cbc:InvoicedQuantity>
  </cac:InvoiceLine>
</Invoice>

这是生成测试数据的代码:

CREATE TABLE [dbo].[Rechnungen](
    [id] [int]  NOT NULL,
    [Nummer] [nvarchar](20) NOT NULL,
    [Datum] [datetime] NOT NULL
)

INSERT INTO Rechnungen (id, Nummer, Datum) VALUES (8, 'R200001', '29.06.2020')

CREATE TABLE [dbo].Rechnungpos(
    [id] [int]  NOT NULL,
    [id_Rechnung] [int] NOT NULL,
    [Anzahl] [float] NOT NULL
)

INSERT INTO RechnungPos (id, id_Rechnung, Anzahl) VALUES (1, 8, 3) 
INSERT INTO RechnungPos (id, id_Rechnung, Anzahl) VALUES (5, 8, 1) 
INSERT INTO RechnungPos (id, id_Rechnung, Anzahl) VALUES (9, 8, 2) 

它必须在不同版本上运行 - 我的版本是 SQL Server 2019

我该怎么做?

谢谢你的帮助,托马斯。

【问题讨论】:

在提出问题时,您需要提供一个可重现的最小示例。请参考以下链接:***.com/help/minimal-reproducible-example 请提供以下内容: (1) DDL 和样本数据填充,即 CREATE table(s) 加上 INSERT T-SQL 语句。 (2) 你需要做什么,即逻辑和你的代码尝试实现它。 (3) 期望的输出,基于上面#1 中的样本数据。 (4) 你的 SQL Server 版本 (SELECT @@version;) Thomas,我之前评论中的 (1) 和 (4) 仍然丢失。请提供。 感谢您的回答 - 我现在制作 1 和 4 - 感谢您的帮助 【参考方案1】:

这里是怎么做的。

唯一的复杂性是如何处理输出 XML 中的子表和特别是命名空间。这就是使用 XQuery 及其 FLWOR 表达式的原因。

SQL

-- DDL and sample data population, start
DECLARE @Rechnungen TABLE (id int , Nummer nvarchar(20), Datum datetime);
INSERT INTO @Rechnungen (id, Nummer, Datum) VALUES 
(8, 'R200001', '2020-06-29');

DECLARE @Rechnungpos TABLE (id int, id_Rechnung int, Anzahl float);
INSERT INTO @RechnungPos (id, id_Rechnung, Anzahl) VALUES 
(1, 8, 3),
(5, 8, 1), 
(9, 8, 2);
-- DDL and sample data population, end

DECLARE @ID_Rechnung int = 8;

;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 (
SELECT '2.1'               AS [cbc:UBLVersionID],
    'TR1.2'                AS [cbc:CustomizationID],
    ''                     AS [cbc:ProfileID],
    p.Nummer               AS [cbc:ID],
    'false'                AS [cbc:CopyIndicator],
    ''                     AS [cbc:UUID],
    CAST(p.Datum AS Date)  AS [cbc:IssueDate],
    (
        SELECT c.id AS [cbc:ID]
            , CAST(c.Anzahl AS INT) AS [cbc:InvoicedQuantity] 
        FROM @Rechnungpos AS c INNER JOIN 
            @Rechnungen AS p ON p.id = c.id_Rechnung
        FOR XML PATH('r'), TYPE, ROOT('root')
    )
FROM @Rechnungen AS p
WHERE p.id = @ID_Rechnung
FOR XML PATH(''), TYPE, ROOT('Invoice')
).query('<Invoice xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
         xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
         xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
         xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
<ext:UBLExtensions>
    <ext:UBLExtension>
        <ext:ExtensionContent/>
    </ext:UBLExtension>
</ext:UBLExtensions>

   for $x in /Invoice/*[local-name()!="root"]
   return $x,
     for $x in /Invoice/root/r
     return <cac:InvoiceLine>$x/*</cac:InvoiceLine>

</Invoice>');

输出

<Invoice xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
  <ext:UBLExtensions>
    <ext:UBLExtension>
      <ext:ExtensionContent />
    </ext:UBLExtension>
  </ext:UBLExtensions>
  <cbc:UBLVersionID>2.1</cbc:UBLVersionID>
  <cbc:CustomizationID>TR1.2</cbc:CustomizationID>
  <cbc:ProfileID />
  <cbc:ID>R200001</cbc:ID>
  <cbc:CopyIndicator>false</cbc:CopyIndicator>
  <cbc:UUID />
  <cbc:IssueDate>2020-06-29</cbc:IssueDate>
  <cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:InvoicedQuantity>3</cbc:InvoicedQuantity>
  </cac:InvoiceLine>
  <cac:InvoiceLine>
    <cbc:ID>5</cbc:ID>
    <cbc:InvoicedQuantity>1</cbc:InvoicedQuantity>
  </cac:InvoiceLine>
  <cac:InvoiceLine>
    <cbc:ID>9</cbc:ID>
    <cbc:InvoicedQuantity>2</cbc:InvoicedQuantity>
  </cac:InvoiceLine>
</Invoice>

【讨论】:

感谢您的解决方案 - 它适用于测试数据。但是,如果我在我需要的 xm 中使用来自 Rechnungen 的所有字段进行尝试,那么我会收到一条错误消息:无法调用 nvarchar (max) 的方法。 (我从德语翻译成英语)并且标记了 .query 命令 我找到了解决方案 - 我将所有选定的数据放在 XML 类型的变量 @XMLData 之后 id 为:SELECT @XMLData.query @Thomas,很高兴听到您找到了完整的解决方案,尽管它似乎过于复杂。请在 LinkedIn 上与我联系。【参考方案2】:

感谢 Yitzhak Khabinsky 的帮助 - 这是完整的解决方案:

DECLARE @ID_Rechnung int = 8,
    @XMLData xml;

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',
            ''                                          AS 'cbc:ProfileID',
            Rechnungen.Nummer                           AS 'cbc:ID',
            'false'                                     AS 'cbc:CopyIndicator',
            ''                                          AS 'cbc:UUID',
            CAST(Rechnungen.Datum AS Date)              AS 'cbc:IssueDate',
            ''                                          AS 'cbc:InvoiceTypeCode',
            Rechnungen.Bemerkung1                       AS 'cbc:Note',
            @Waehrung                                   AS 'cbc:DocumentCurrencyCode',
            @Waehrung                                   AS 'cbc:TaxCurrencyCode',
            Rechnungen.Auftrag                          AS 'cac:OrderReference/cbc:ID',
            -- Verkaüfer
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cbc:EndpointID',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyIdentification/cbc:ID',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:StreetName',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:CityName',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cbc:PostalZone',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyId',
            'VAT'                                       AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyLegalForm',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Name',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:Telephone',
            ''                                          AS 'cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail',
            -- Käufer
            ''                                          AS 'cac:AccountingCustomerParty/cac:Party/cbc:EndpointID',
            Rechnungen.DebKreNr                         AS 'cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID',
            Rechnungen.DebBez01 + ' ' + DebBez02        AS 'cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name',
            Rechnungen.DebStrasse                       AS 'cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:StreetName',
            Rechnungen.DebOrt                           AS 'cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:CityName',
            Rechnungen.DebPLZ                           AS 'cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cbc:PostalZone',
            Rechnungen.DebLandKFZ                       AS 'cac:AccountingCustomerParty/cac:Party/cac:PostalAddress/cac:Country/cbc:IdentificationCode',
            Rechnungen.DebUMSTID                        AS 'cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID',
            'VAT'                                       AS 'cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID',
            Rechnungen.DebBez01 + ' ' + DebBez02        AS 'cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName',
            ''                                          AS 'cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:Name',
            ''                                          AS 'cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:Telephone',
            ''                                          AS 'cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail',
            -- Kontoverbindung Verkäufer
            ''                                          AS 'cac:PaymentMeans/cbc:PaymentMeansCode',
            ''                                          AS 'cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID',
            ''                                          AS 'cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:Name',
            ''                                          AS 'cac:PaymentMeans/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID',
            --''                                            AS 'cac:PaymentTerms/cbc:Note',
            -- Steuern
            @Waehrung                                   AS 'cac:TaxTotal/cbc_TaxAmount/@currencyID',
            CAST(Rechnungen.BetragMWST AS nvarchar(15)) AS 'cac:TaxTotal/cbc_TaxAmount',
            @Waehrung                                   AS 'cac:TaxTotal/cac:Taxubtotal/cbc:TaxableAmount/@currencyID',
            CAST(Rechnungen.BetragNetto AS nvarchar(15))AS 'cac:TaxTotal/cac:Taxubtotal/cbc:TaxableAmount',
            @Waehrung                                   AS 'cac:TaxTotal/cac:Taxubtotal/cbc:TaxAmount/@currencyID',
            CAST(Rechnungen.BetragMWST  AS nvarchar(15))AS 'cac:TaxTotal/cac:Taxubtotal/cbc:TaxAmount',
            ''                                          AS 'cac:TaxTotal/cac:Taxubtotal/cac:TaxCategory/cbc:ID',
            CAST(Rechnungen.MWST AS nvarchar(2))        AS 'cac:TaxTotal/cac:Taxubtotal/cac:TaxCategory/cbc:Percent',
            'VAT'                                       AS 'cac:TaxTotal/cac:Taxubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID',
            @Waehrung                                   AS 'cac:LegalMonetaryTotal/cbc:LineExtensionAmount/@currencyID',
            CAST(Rechnungen.BetragNetto AS nvarchar(15))AS 'cac:LegalMonetaryTotal/cbc:LineExtensionAmount',
            @Waehrung                                   AS 'cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount/@currencyID',
            CAST(Rechnungen.BetragNetto AS nvarchar(15))AS 'cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount',
            @Waehrung                                   AS 'cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount/@currencyID',
            CAST(Rechnungen.BetragBrutto AS nvarchar(15))AS 'cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount',
            @Waehrung                                   AS 'cac:LegalMonetaryTotal/cbc:PayableAmount/@currencyID',
            CAST(Rechnungen.BetragBrutto AS nvarchar(15))AS 'cac:LegalMonetaryTotal/cbc:PayableAmount',

    (
        SELECT Rechnungpos.id AS [cbc:ID]
            , CAST(Rechnungpos.Anzahl AS INT) AS [cbc:InvoicedQuantity] 
        FROM Rechnungpos WHERE RechnungPos.id_Rechnung = @id_Rechnung
        FOR XML PATH('r'), TYPE, ROOT('root')
    )
FROM Rechnungen 
WHERE Rechnungen.id = @ID_Rechnung
FOR XML PATH(''), TYPE, ROOT('Invoice')

) AS xmldataCol
) AS xmldat;

SELECT @XMLData
.query('<Invoice xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"
         xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
         xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
         xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">

   for $x in /Invoice/*[local-name()!="root"]
   return $x,
     for $x in /Invoice/root/r
     return <cac:InvoiceLine>$x/*</cac:InvoiceLine>

</Invoice>');

【讨论】:

以上是关于SQL XML - 从 SQL Server 为发票创建一个 XML 文件,包括一个 XML 文件中的发票位置的主要内容,如果未能解决你的问题,请参考以下文章

从sql server中的xml中提取数据

将数据从 XML 导入 SQL Server 表

将 XML 文档从 SQL SERVER 插入到 Oracle

如何使用 Spark 从 XML 复制到 SQL Server

如何从 SQL Server 中的 XML 元素获取特定属性

从SQL Server的XML数据类型字段中提取数据