XML生成的一个特例

时间:2014-04-28 15:04:54

标签: sql sql-server xml

我正在使用SQL Server 2012 SP1 Express:

如何生成此XML?

<Batch CodeGeneratorId="1234" BatchNumber="Batch0001" OrderNumber="Order0001" TimeStamp="2012-01-01 05:15:00">
    <BatchDetail Key="Name" Value="MyName" />
    <BatchDetail Key="Stu" Value="FA20123.01" />
    <BatchDetail Key="Com_L1" Value="19898" />
    <BatchDetail Key="Dec_L1" Value="24" />
    <BatchDetail Key="Agg_L1" Value="18520" />
</Batch>

使用BATCH_DATA表上的数据:

ID  |          Key     |    Value
----+------------------+-------------
1   | CodeGeneratorId  | 1234
2   | BatchNumber      | Batch0001
3   | OrderNumber      | Order0001
4   | Name             | MyName
5   | Stu              | FA20123.01
6   | Com_L1           | 19898
7   | Dec_L1           | 24
8   | Agg_L1           | 18520

我的问题是我不知道如何生成这部分:

<Batch CodeGeneratorId="1234" BatchNumber="Batch0001" 
       OrderNumber="Order0001" TimeStamp="2012-01-01 05:15:00">

2 个答案:

答案 0 :(得分:1)

我能够以您需要的格式输出以下内容:

CREATE TABLE #T ([ID] INT, [Key] VARCHAR(15), [Value] VARCHAR(10));

INSERT INTO #T ([ID], [Key], [Value])
VALUES
    (1, 'CodeGeneratorId', '1234'),
    (2, 'BatchNumber', 'Batch0001'),
    (3, 'OrderNumber', 'Order0001'),
    (4, 'Name', 'MyName'),
    (5, 'Stu', 'FA20123.01'),
    (6, 'Com_L1', '19898'),
    (7, 'Dec_L1', '24'),
    (8, 'Agg_L1', '18520');


SELECT  [Batch].[CodeGeneratorId], 
        [Batch].[BatchNumber], 
        [Batch].[OrderNumber], 
        BatchDetails.[Key], 
        BatchDetails.Value
FROM    (   SELECT  [CodeGeneratorId], [BatchNumber], [OrderNumber]
            FROM    (   SELECT  [Key], [Value]
                        FROM    #T
                        WHERE   [Key] IN ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
                    ) t
                    PIVOT 
                    (   MAX(Value)
                        FOR [Key] IN ([CodeGeneratorId], [BatchNumber], [OrderNumber])
                    ) pvt
        ) AS [Batch]
        CROSS JOIN
        (   SELECT  [Key], Value
            FROM    #T AS BatchDetail 
            WHERE   [Key] NOT IN ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
        ) AS BatchDetails
FOR XML AUTO;

它本质上是一个两阶段的过程,首先创建标题:

SELECT  [CodeGeneratorId], [BatchNumber], [OrderNumber]
FROM    (   SELECT  [Key], [Value]
            FROM    #T
            WHERE   [Key] IN ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
        ) t
        PIVOT 
        (   MAX(Value)
            FOR [Key] IN ([CodeGeneratorId], [BatchNumber], [OrderNumber])
        ) pvt
FOR XML AUTO;

会给:

<pvt CodeGeneratorId="1234" BatchNumber="Batch0001" OrderNumber="Order0001" />


SELECT  [Key], Value
FROM    #T AS BatchDetail 
WHERE   [Key] NOT IN ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
FOR XML AUTO;

会给:

<BatchDetail Key="Name" Value="MyName" />
<BatchDetail Key="Stu" Value="FA20123.01" />
<BatchDetail Key="Com_L1" Value="19898" />
<BatchDetail Key="Dec_L1" Value="24" />
<BatchDetail Key="Agg_L1" Value="18520" />

所以这只是将两者结合起来的问题。如果您实际上有一个要链接的字段,则可能需要将其从交叉连接转换为交叉应用:

CREATE TABLE #T2 ([ID] INT, Col INT, [Key] VARCHAR(15), [Value] VARCHAR(10));

INSERT INTO #T2 ([ID], Col, [Key], [Value])
VALUES
    (1, 1, 'CodeGeneratorId', '1234'),
    (2, 1, 'BatchNumber', 'Batch0001'),
    (3, 1, 'OrderNumber', 'Order0001'),
    (4, 1, 'Name', 'MyName'),
    (5, 1, 'Stu', 'FA20123.01'),
    (6, 1, 'Com_L1', '19898'),
    (7, 1, 'Dec_L1', '24'),
    (8, 1, 'Agg_L1', '18520'),
    (9, 2, 'CodeGeneratorId', '1255'),
    (10, 2, 'BatchNumber', 'Batch0002'),
    (11, 2, 'OrderNumber', 'Order0002'),
    (12, 2, 'Name', 'MyName'),
    (13, 2, 'Stu', 'FA20123.01'),
    (14, 2, 'Com_L1', '19898'),
    (15, 2, 'Dec_L1', '24'),
    (16, 2, 'Agg_L1', '18520');


SELECT  [Batch].[CodeGeneratorId], 
        [Batch].[BatchNumber], 
        [Batch].[OrderNumber], 
        BatchDetails.[Key], 
        BatchDetails.Value
FROM    (   SELECT  Col, [CodeGeneratorId], [BatchNumber], [OrderNumber]
            FROM    (   SELECT  Col, [Key], [Value]
                        FROM    #T2
                        WHERE   [Key] IN ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
                    ) t
                    PIVOT 
                    (   MAX(Value)
                        FOR [Key] IN ([CodeGeneratorId], [BatchNumber], [OrderNumber])
                    ) pvt
        ) AS [Batch]
        CROSS APPLY
        (   SELECT  [Key], Value
            FROM    #T2 AS BatchDetail 
            WHERE   [Key] NOT IN ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
            AND     BatchDetail.Col = Batch.Col
        ) AS BatchDetails
FOR XML AUTO;

会给你两个结果:

<Batch CodeGeneratorId="1234" BatchNumber="Batch0001" OrderNumber="Order0001">
  <BatchDetails Key="Name" Value="MyName" />
  <BatchDetails Key="Stu" Value="FA20123.01" />
  <BatchDetails Key="Com_L1" Value="19898" />
  <BatchDetails Key="Dec_L1" Value="24" />
  <BatchDetails Key="Agg_L1" Value="18520" />
</Batch>
<Batch CodeGeneratorId="1255" BatchNumber="Batch0002" OrderNumber="Order0002">
  <BatchDetails Key="Name" Value="MyName" />
  <BatchDetails Key="Stu" Value="FA20123.01" />
  <BatchDetails Key="Com_L1" Value="19898" />
  <BatchDetails Key="Dec_L1" Value="24" />
  <BatchDetails Key="Agg_L1" Value="18520" />
</Batch>

答案 1 :(得分:1)

您可以将表转换为XML,然后查询该XML以构建所需的XML。

select T.X.value('(/row[Key = "CodeGeneratorId"]/Value/text())[1]', 'varchar(50)') as [@CodeGeneratorId],
       T.X.value('(/row[Key = "BatchNumber"]/Value/text())[1]', 'varchar(50)') as [@BatchNumber],
       T.X.value('(/row[Key = "OrderNumber"]/Value/text())[1]', 'varchar(50)') as [@OrderNumber],
       sysdatetime() as [@TimeStamp],
       (
       select R.X.value('(Key/text())[1]', 'varchar(50)') as [@Name],
              R.X.value('(Value/text())[1]', 'varchar(50)') as [@Value]
       from T.X.nodes('row') as R(X)
       where R.X.value('(Key/text())[1]', 'varchar(50)') not in ('CodeGeneratorId', 'BatchNumber', 'OrderNumber')
       for xml path('BatchDetail'), type
       )
from (
     select [Key],
            Value
     from T
     for xml path('row'), type
     ) as T(X)
for xml path('Batch'), type

SQL Fiddle