将XML文本转换为Sql表...然后再返回

时间:2017-03-16 13:35:09

标签: sql-server xml data-conversion

我有一张桌子:

with XMLData as (
    SELECT uid, commonname, cast(labeldetails as XML) labelxml
    FROM [MyLables]
)
SELECT 
     uid
    ,commonname
    ,labelxml
FROM
    XMLData [x]

我得到了(为了简洁起见):

B8A3DF5E    OrderType1  <NewDataSet><LabelData><Name>1d Vert</Name><Column>...
9D0F94C7    OrderType2  <NewDataSet><LabelData><Name>ItemNumber1D</Name><Co...

内部标签详细信息是XML数据

<NewDataSet>
    <LabelData>
        <Name>mol</Name>
        <Column>mol</Column>
        <Type>MOLIMAGE</Type>
        <xpos>510</xpos>
        <ypos>110</ypos>
        <width>auto</width>
        <height>auto</height>
        <Font>Arial</Font>
        <Fontsize>10.0</Fontsize>
        <FontStyle>Normal</FontStyle>
        <Caption />
        <_x0032_DBarcode_Margin>1</_x0032_DBarcode_Margin>
        <_x0032_DBarcode_ModSize>5</_x0032_DBarcode_ModSize>
        <MOL_WIDTH>200</MOL_WIDTH>
        <MOL_HEIGHT>200</MOL_HEIGHT>
        <_x0020_MOL_MARGIN>15</_x0020_MOL_MARGIN>
        <MOL_BONDLINEWIDTH>2</MOL_BONDLINEWIDTH>
        <MOL_BONDSPACEING>5</MOL_BONDSPACEING>
        <MOL_FONTSIZE>15</MOL_FONTSIZE>
        <xpos_Inches>150</xpos_Inches>
        <ypos_Inches>600</ypos_Inches>
        <width_Inches>110</width_Inches>
        <height_Inches>510</height_Inches>
        <LogoImageName>110</LogoImageName>
        <ypos_int>110</ypos_int>
        <xpos_int>510</xpos_int>
    </LabelData>
    ....
    ....
</NewDataSet>

我想将LabelDetails转换为XML并创建一个具有各行的临时表:

uid    commonname   id    name  column  type        xpos    ypox   ...
1234   OrderType1   1     col   col     TEXT        5       5      ...
1234   OrderType1   2     mol   mol     MOLIMAGE    1       1      ...
6789   OrderType2   1     col   col     TEXT        5       5      ...

我不认为所有XML表都有相同的行...假设不是

我已经查看了其他To / From XML问题,其中没有一个看起来合适,我有点不知道下一步该去哪里......

最终,我希望转换为/从XML数据转换,目标是将其作为我们的“系统”存储此数据的旧方式与新方法之间的“间隙”。

我在哪里可以开始创建2个存储过程:FromXMLtoTable和FromTableToXML

2 个答案:

答案 0 :(得分:2)

这将帮助您入门。我不得不做类似的事情。这可能不是一个确切的答案,但有一些代码可以用来将XML数据提供给SQL表。

--Set tempXML for testing
CREATE TABLE #tempXML(id INT IDENTITY(1,1) PRIMARY KEY, xmlData XML)
INSERT INTO #tempXML(xmlData)
VALUES('<NewDataSet>
    <LabelData>
        <Name>mol</Name>
        <Column>mol</Column>
        <Type>MOLIMAGE</Type>
        <xpos>510</xpos>
        <ypos>110</ypos>
        <width>auto</width>
        <height>auto</height>
        <Font>Arial</Font>
        <Fontsize>10.0</Fontsize>
        <FontStyle>Normal</FontStyle>
        <Caption />
        <_x0032_DBarcode_Margin>1</_x0032_DBarcode_Margin>
        <_x0032_DBarcode_ModSize>5</_x0032_DBarcode_ModSize>
        <MOL_WIDTH>200</MOL_WIDTH>
        <MOL_HEIGHT>200</MOL_HEIGHT>
        <_x0020_MOL_MARGIN>15</_x0020_MOL_MARGIN>
        <MOL_BONDLINEWIDTH>2</MOL_BONDLINEWIDTH>
        <MOL_BONDSPACEING>5</MOL_BONDSPACEING>
        <MOL_FONTSIZE>15</MOL_FONTSIZE>
        <xpos_Inches>150</xpos_Inches>
        <ypos_Inches>600</ypos_Inches>
        <width_Inches>110</width_Inches>
        <height_Inches>510</height_Inches>
        <LogoImageName>110</LogoImageName>
        <ypos_int>110</ypos_int>
        <xpos_int>510</xpos_int>
    </LabelData>
</NewDataSet>')

    SELECT r.value('Name[1]', 'nvarchar(100)') AS Field1
            , r.value('Column[1]', 'nvarchar(100)') AS Field2
            , r.value('Type[1]', 'nvarchar(100)') AS Field3
            --etc...
    FROM #tempXML
        CROSS APPLY xmlData.nodes('/NewDataSet/LabelData') AS x(r)
------------------------------------------------------------------------

    DECLARE @loopCount          INT
    DECLARE @recordID           INT
    DECLARE @columnName         NVARCHAR(128)
    DECLARE @dataType           NVARCHAR(10)
    DECLARE @strSQL             NVARCHAR(MAX)
    DECLARE @fieldValue         NVARCHAR(MAX)

    --This table will store your Columns from your new table of xml parsed data
    CREATE TABLE #TableFields
    (
        id int not null identity,
        COLUMN_NAME NVARCHAR(100),
        DATA_TYPE   NVARCHAR(10)
    )

    --Insert column names from xml parsed data to TableFields temp table
    INSERT INTO #TableFields (COLUMN_NAME, DATA_TYPE)
    SELECT COLUMN_NAME, DATA_TYPE
    FROM Information_Schema.Columns 
    WHERE Table_Name = 'Insert Table Here'
            AND COLUMN_NAME <> 'ID'

    --Create your xml parsed table(or use a physical one)
    CREATE TABLE #temptable 
    (
        id INT IDENTITY(1,1), 
        field1 VARCHAR(100), 
        field2 VARCHAR(100),
        field3 VARCHAR(100)
        --etc...
    )

    --Insert the parsed xml from #tempXML test table to #tempTable
    INSERT INTO #temptable(fieldName, fieldValue, xmlID)
    SELECT r.value('Name[1]', 'nvarchar(100)') AS Field1
            , r.value('Column[1]', 'nvarchar(100)') AS Field2
            , r.value('Type[1]', 'nvarchar(100)') AS Field3
            --etc...
    FROM #tempXML
        CROSS APPLY xmlData.nodes('/NewDataSet/LabelData') AS x(r)

    --Set a loopCount for while loop
    SET @loopCount = 1

    --Use the while loop to check if we have any fields left to go through
        while ( exists(SELECT id FROM #TableFields WHERE id = @loopCount) )
            BEGIN

                --Get current record in temp table
                SELECT  @columnName     = t.COLUMN_NAME,
                        @dataType       = t.DATA_TYPE,
                        @fieldValue     = v.fieldValue,
                        @recordID       = v.xmlID
                FROM #TableFields t
                    JOIN #temptable v ON
                        t.id = v.id AND
                        t.COLUMN_NAME = v.fieldName
                WHERE t.id = @loopCount
                -----------------------------------------------------------         

                SET @strSQL = 'UPDATE [insert your table here] SET ' + @columnName + ' = ''' + CONVERT(NVARCHAR(MAX), @fieldValue) + ''' FROM [insert your table here] WHERE ID = ' + CONVERT(NVARCHAR(MAX), @recordID)
                EXEC sp_executesql @strSQL, N'@columnName varchar(128)', @columnName = @columnName


                DELETE FROM #TableFields WHERE id = @loopCount

                SET @loopCount = @loopCount + 1
            END


    DROP TABLE #TableFields
    DROP TABLE #temptable
    DROP TABLE #tempXML

答案 1 :(得分:0)

编写XML

以您描述的方式获取XML非常简单:

SELECT * FROM AnySource FOR XML RAW(N'LabelData'),ELEMENTS,ROOT(N'NewDataSet')

这适用于任何表,视图或表值函数...

阅读XML

如果你的目标是 entity-key-value tupel,那么读取一个未知的XML很容易:

DECLARE @xml XML=
N'<NewDataSet>
    <LabelData>
        <Name>mol</Name>
        <Column>mol</Column>
        <Type>MOLIMAGE</Type>
        <!--More elements-->
        <TEST1>Only existing in 1</TEST1>
    </LabelData>
    <LabelData>
        <Name>2nd name</Name>
        <Column>2nd col</Column>
        <Type>2nd type</Type>
        <!--More elements-->
        <TEST2>Only existing in 2</TEST2>
    </LabelData>
</NewDataSet>';

WITH NewDataSet aS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS OrdPosition
          ,ld.query('./*') AS LabelDataElements
    FROM @xml.nodes(N'/NewDataSet/LabelData') AS A(ld)
)
SELECT ds.OrdPosition
      ,AllNodes.value(N'local-name(.)',N'nvarchar(max)') AS ElementName
      ,AllNodes.value(N'(./text())[1]',N'nvarchar(max)') AS ElementValue
FROM NewDataSet AS ds
CROSS APPLY ds.LabelDataElements.nodes(N'*') AS A(AllNodes);

结果

+-------------+-------------+--------------------+
| OrdPosition | ElementName | ElementValue       |
+-------------+-------------+--------------------+
| 1           | Name        | mol                |
+-------------+-------------+--------------------+
| 1           | Column      | mol                |
+-------------+-------------+--------------------+
| 1           | Type        | MOLIMAGE           |
+-------------+-------------+--------------------+
| 1           | TEST1       | Only existing in 1 |
+-------------+-------------+--------------------+
| 2           | Name        | 2nd name           |
+-------------+-------------+--------------------+
| 2           | Column      | 2nd col            |
+-------------+-------------+--------------------+
| 2           | Type        | 2nd type           |
+-------------+-------------+--------------------+
| 2           | TEST2       | Only existing in 2 |
+-------------+-------------+--------------------+

但是将其写入以local-name()作为列标题的表中更为棘手。有两种情况:

  • 您事先知道所有可能的列名,即使不是每个列都必须包含所有列名称。
  • 您的XML包含各种不同的元素,并且您事先并不知道这些名称。

已知列名

一个很大的优势:您 - 可能 - 了解数据类型,您可以正确读取/转换所有值!

列命名可以使用PIVOT分组聚合完成,有大量示例可供查找...

使用SELECT ... INTO SomeStagingTable FROM ...即时创建表格。

未知的列名

在这种情况下,我会坚持使用 entity-key-value tupels列表。您可以将动态创建的SQL用于动态定义的PIVOT方法(很多示例!),但这在以后的查询中很难使用。在物理表中没有多大意义,你不知道结构...