从 SQL 服务器 table 到 XML 的数据

Data from SQL Server table to XML

我有 2 张桌子

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>

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

虽然使用 EXPLICIT 可能比对应的 RAWAUTO 更冗长,但它可以轻松地为您提供使用具有特定属性的嵌套节点的输出所需的控制级别.重要的是要注意,有 Tag(作为整数)和 Parent 列分别指示标签 ID 及其关联的父级。其余列使用列命名格式 <NODE_NAME>!<TagId>!<AttributeName>。理想情况下,您会在不需要数据的地方输出 NULL 值(我在下面使用案例表达式来实现此目的),因为这会导致返回给客户端的数据较少,但无论哪种方式都有效,我已经包含了几个您可以从下面的备选方案中进行选择,并提供一个工作演示 fiddle。我还根据您想要的 DataType 或节点使用 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

让我知道这是否适合你。

这是完成任务的更简单的方法。

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

此方法允许分两步组成XML'visually'

  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>