【问题标题】:Data from SQL Server table to XML从 SQL Server 表到 XML 的数据
【发布时间】:2021-11-07 16:14:49
【问题描述】:

我有两张桌子

CREATE TABLE [dbo].[TestData] 
(
    [Data] [varchar](2000) NULL,
    [DataType] AS (SUBSTRING([Data], (3), (3)))
)

CREATE TABLE [dbo].[TestDataTypes] 
(
    DataTypeID varchar(3) NOT NULL,
    DateTypeName varchar(50) NOT NULL
)

这是一些测试数据:

INSERT INTO TestDataTypes (DataTypeID, DateTypeName) 
VALUES ('010', 'DataHeader'),
       ('020', 'SectionHeader'),
       ('030', 'SectionData'),
       ('080', 'SectionFooter'),
       ('090','DataFooter');

INSERT INTO TestData ([Data]) 
VALUES ('FI0103146701200242606660000000000000000000020210908074601P'),
       ('FI020836740675301000031012700000000000000000000000020210908074601'),
       ('FI03020210907710000000002395847961930920210907018990006320210908000000000689708 CC000000000N'),
       ('FI03020210907710000000002396398519301520210907395125022320210908000000000016036 CC000000000N'),
       ('FI03020210907710000000002392918856530120210907738990002520210908000000000024424 CC000000000N'),
       ('FI0808367406700000000154000000023679373000000000000000'),
       ('FI09031467012002426066600000000000000000000100000000154');

现在我可以加入我的数据了

SELECT
    TD.DataType, TDT.DateTypeName, TD.[data] 
FROM
    TestData AS TD
INNER JOIN
    TestDataTypes TDT ON TD.DataType = TDT.DataTypeID

输出:

010   DataHeader      FI0103146701200242606660000000000000000000020210908074601P
020   SectionHeader   FI020836740675301000031012700000000000000000000000020210908074601
030   SectionData     FI03020210907710000000002395847961930920210907018990006320210908000000000689708 CC000000000N
030   SectionData     FI03020210907710000000002396398519301520210907395125022320210908000000000016036 CC000000000N
030   SectionData     FI03020210907710000000002392918856530120210907738990002520210908000000000024424 CC000000000N
080   SectionFooter   FI0808367406700000000154000000023679373000000000000000
090   DataFooter      FI09031467012002426066600000000000000000000100000000154

我怎样才能让它在 XML 中变成这样:

<DataHeader DataType="010" girodata="FI0103146701200242606660000000000000000000020210908074601P" >
    <SectionHeader DataType="020" girodata="FI020836740675301000031012700000000000000000000000020210908074601" >
        <SectionData DataType="030" girodata="FI03020210907710000000002395847961930920210907018990006320210908000000000689708 CC000000000N" ></SectionData>
        <SectionData DataType="030" girodata="FI03020210907710000000002396398519301520210907395125022320210908000000000016036 CC000000000N" ></SectionData>
        <SectionData DataType="030" girodata="FI03020210907710000000002392918856530120210907738990002520210908000000000024424 CC000000000N" ></SectionData>
    </SectionHeader>
    <SectionFooter DataType="080" girodata="FI0808367406700000000154000000023679373000000000000000"></SectionFooter>
    <DataFooter DataType="090" girodata="FI09031467012002426066600000000000000000000100000000154"></GiroFooter>
</DataHeader>

【问题讨论】:

    标签: sql sql-server xml etl


    【解决方案1】:

    这里有一个更容易完成任务的方法。

    它使用标准 XML API:XQuery 及其 FLWOR 表达式。

    此方法允许通过两步'可视化'编写 XML:

    1. 通过FOR XML ... 创建原始 XML。
    2. 通过 XQuery 编写准确的输出 XML。

    SQL

    -- DDL and sample data population, start
    DECLARE @TestData TABLE ([Data] [varchar](2000) NULL, [DataType] AS (SUBSTRING([Data], (3), (3))));
    INSERT INTO @TestData ([Data]) VALUES 
    ('FI0103146701200242606660000000000000000000020210908074601P'),
    ('FI020836740675301000031012700000000000000000000000020210908074601'),
    ('FI03020210907710000000002395847961930920210907018990006320210908000000000689708 CC000000000N'),
    ('FI03020210907710000000002396398519301520210907395125022320210908000000000016036 CC000000000N'),
    ('FI03020210907710000000002392918856530120210907738990002520210908000000000024424 CC000000000N'),
    ('FI0808367406700000000154000000023679373000000000000000'),
    ('FI09031467012002426066600000000000000000000100000000154');
    -- DDL and sample data population, end
    
    SELECT (
        SELECT * FROM @TestData
        FOR XML PATH('r'), TYPE, ROOT('root')
    ).query('<DataHeader DataType="010" girodata="{/root/r[DataType/text()="010"]/Data}" >
        <SectionHeader DataType="020" girodata="{/root/r[DataType/text()="020"]/Data}" >
        {
            for $x in /root/r[DataType/text()="030"]
            return <SectionData DataType="030" girodata="{$x/Data}"></SectionData>
        }
        </SectionHeader>
        <SectionFooter DataType="080" girodata="{/root/r[DataType/text()="080"]/Data}"></SectionFooter>
        <DataFooter DataType="090" girodata="{/root/r[DataType/text()="090"]/Data}"></DataFooter>
    </DataHeader>');
    

    输出

    <DataHeader DataType="010" girodata="FI0103146701200242606660000000000000000000020210908074601P">
      <SectionHeader DataType="020" girodata="FI020836740675301000031012700000000000000000000000020210908074601">
        <SectionData DataType="030" girodata="FI03020210907710000000002395847961930920210907018990006320210908000000000689708 CC000000000N" />
        <SectionData DataType="030" girodata="FI03020210907710000000002396398519301520210907395125022320210908000000000016036 CC000000000N" />
        <SectionData DataType="030" girodata="FI03020210907710000000002392918856530120210907738990002520210908000000000024424 CC000000000N" />
      </SectionHeader>
      <SectionFooter DataType="080" girodata="FI0808367406700000000154000000023679373000000000000000" />
      <DataFooter DataType="090" girodata="FI09031467012002426066600000000000000000000100000000154" />
    </DataHeader>
    

    【讨论】:

      【解决方案2】:

      您可以使用FOR XML EXPLICIT 来实现此目的。

      虽然使用EXPLICIT 可能比其对应的RAWAUTO 更冗长,但它很容易为您提供使用具有特定属性的嵌套节点的输出所需的控制级别。需要注意的是,有 Tag(整数)和 Parent 列分别表示标签 ID 及其关联的父级。其余列使用列命名格式&lt;NODE_NAME&gt;!&lt;TagId&gt;!&lt;AttributeName&gt;。理想情况下,您会在不需要数据的情况下输出 NULL 值(我在下面使用了 case 表达式来实现这一点),因为这会导致返回给客户端的数据更少,但无论哪种方式都可以,我在下面提供了几个替代方案您可以从中选择一个工作演示小提琴。我还根据您想要的 DataTypes 或节点使用 where 子句进行过滤。

      推荐

      SELECT
           CAST(TD.DataType AS INT) as Tag,
           CASE 
               WHEN TD.DataType='010' THEN NULL 
               WHEN TD.DataType='030' THEN 20
               ELSE 10 
           END as Parent,
           
           CASE WHEN TD.DataType='010' THEN TD.DataType END as 'DataHeader!010!DataType',
           CASE WHEN TD.DataType='010' THEN TD.[data] END as 'DataHeader!010!girodata',
           CASE WHEN TD.DataType='020' THEN TD.DataType END as 'SectionHeader!020!DataType',
           CASE WHEN TD.DataType='020' THEN TD.[data] END  as 'SectionHeader!020!girodata',
           
           CASE WHEN TD.DataType='030' THEN TD.DataType END as 'SectionData!030!DataType',
           CASE WHEN TD.DataType='030' THEN TD.[data] END as 'SectionData!030!girodata',
           
           CASE WHEN TD.DataType='080' THEN TD.DataType END as 'SectionFooter!080!DataType',
           CASE WHEN TD.DataType='080' THEN TD.[data] END as 'SectionFooter!080!girodata',
           CASE WHEN TD.DataType='090' THEN TD.DataType END as 'DataFooter!090!DataType',
           CASE WHEN TD.DataType='090' THEN TD.[data] END as 'DataFooter!090!girodata'
      FROM TestData as TD 
      WHERE TD.DataType IN ('010','020','080','090','030')
      FOR XML EXPLICIT
      

      其他替代品

      WITH my_data AS (
           SELECT 
               TD.DataType,TDT.DateTypeName,TD.[data] as dataval
           FROM TestData as TD 
           INNER JOIN TestDataTypes TDT on TD.DataType = TDT.DataTypeID
           WHERE DataType IN ('010','020','080','090','030')
      )
      SELECT
           CAST(DataType AS INT) as Tag,
           CASE 
               WHEN DateTypeName='DataHeader' THEN NULL 
               WHEN DateTypeName='SectionData' THEN 20
               ELSE 10 
           END as Parent,
           
           CASE WHEN DateTypeName='DataHeader' THEN DataType END as 'DataHeader!010!DataType',
           CASE WHEN DateTypeName='DataHeader' THEN dataval END as 'DataHeader!010!girodata',
           CASE WHEN DateTypeName='SectionHeader' THEN DataType END as 'SectionHeader!020!DataType',
           CASE WHEN DateTypeName='SectionHeader' THEN dataval END as 'SectionHeader!020!girodata',
           
           CASE WHEN DateTypeName='SectionData' THEN DataType END as 'SectionData!030!DataType',
           CASE WHEN DateTypeName='SectionData' THEN dataval END as 'SectionData!030!girodata',
           
           CASE WHEN DateTypeName='SectionFooter' THEN DataType END as 'SectionFooter!080!DataType',
           CASE WHEN DateTypeName='SectionFooter' THEN dataval END as 'SectionFooter!080!girodata',
           CASE WHEN DateTypeName='DataFooter' THEN DataType END as 'DataFooter!090!DataType',
           CASE WHEN DateTypeName='DataFooter' THEN dataval END as 'DataFooter!090!girodata'
      FROM my_data
      FOR XML EXPLICIT
      

      或者不那么冗长

      WITH my_data AS (
           SELECT 
               TD.DataType,TDT.DateTypeName,TD.[data] as dataval
           FROM TestData as TD 
           INNER JOIN TestDataTypes TDT on TD.DataType = TDT.DataTypeID
           WHERE DataType IN ('010','020','080','090','030')
      )
      SELECT
           CAST(DataType AS INT) as Tag,
           CASE 
               WHEN DateTypeName='DataHeader' THEN NULL 
               WHEN DateTypeName='SectionData' THEN 20
               ELSE 10 
           END as Parent,
           
           DataType as 'DataHeader!010!DataType',
           dataval  as 'DataHeader!010!girodata',
           DataType as 'SectionHeader!020!DataType',
           dataval  as 'SectionHeader!020!girodata',
           
           DataType as 'SectionData!030!DataType',
           dataval as 'SectionData!030!girodata',
           
           DataType as 'SectionFooter!080!DataType',
           dataval as 'SectionFooter!080!girodata',
           DataType as 'DataFooter!090!DataType',
           dataval as 'DataFooter!090!girodata'
      FROM my_data
      FOR XML EXPLICIT
      

      SELECT
           CAST(TD.DataType AS INT) as Tag,
           CASE 
               WHEN TDT.DateTypeName='DataHeader' THEN NULL 
               WHEN TDT.DateTypeName='SectionData' THEN 20
               ELSE 10 
           END as Parent,
           
           TD.DataType as 'DataHeader!010!DataType',
           TD.[data]  as 'DataHeader!010!girodata',
           TD.DataType as 'SectionHeader!020!DataType',
           TD.[data]  as 'SectionHeader!020!girodata',
           
           TD.DataType as 'SectionData!030!DataType',
           TD.[data] as 'SectionData!030!girodata',
           
           TD.DataType as 'SectionFooter!080!DataType',
           TD.[data] as 'SectionFooter!080!girodata',
           TD.DataType as 'DataFooter!090!DataType',
           TD.[data] as 'DataFooter!090!girodata'
      FROM TestData as TD 
      INNER JOIN TestDataTypes TDT on TD.DataType = TDT.DataTypeID
      WHERE TD.DataType IN ('010','020','080','090','030')
      FOR XML EXPLICIT
      

      没有连接会更高效

      SELECT
           CAST(TD.DataType AS INT) as Tag,
           CASE 
               WHEN TD.DataType='010' THEN NULL 
               WHEN TD.DataType='030' THEN 20
               ELSE 10 
           END as Parent,
           
           TD.DataType as 'DataHeader!010!DataType',
           TD.[data]  as 'DataHeader!010!girodata',
           TD.DataType as 'SectionHeader!020!DataType',
           TD.[data]  as 'SectionHeader!020!girodata',
           
           TD.DataType as 'SectionData!030!DataType',
           TD.[data] as 'SectionData!030!girodata',
           
           TD.DataType as 'SectionFooter!080!DataType',
           TD.[data] as 'SectionFooter!080!girodata',
           TD.DataType as 'DataFooter!090!DataType',
           TD.[data] as 'DataFooter!090!girodata'
      FROM TestData as TD 
      WHERE TD.DataType IN ('010','020','080','090','030')
      FOR XML EXPLICIT
      

      工作演示数据库fiddle here

      让我知道这是否适合你。

      【讨论】:

        猜你喜欢
        • 2013-09-08
        • 2013-08-28
        • 1970-01-01
        • 1970-01-01
        • 2016-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多