以特定格式

时间:2018-02-27 17:47:41

标签: xml tsql sql-server-2012 xml-parsing xquery

我有以XML格式存储的数据,其格式多于HTML格式,而不是真正的XML格式。元素头包含列名,行元素包含这些列的数据。

<root>
<head>
    <value>DeviceID</value>
    <value>VolumeName</value>
    <value>SizeGB</value>
    <value>FreeSpaceGB</value>
    <value>FreeSpacePercent</value>
</head>
<row>
    <value>C:</value>
    <value>c$OS-COMPUTER01</value>
    <value>126</value>
    <value>43</value>
    <value>34</value>
</row>
<row>
    <value>D:</value>
    <value>D$DB-COMPUTER01</value>
    <value>127</value>
    <value>104</value>
    <value>82</value>
</row>
<row>
    <value>E:</value>
    <value>E$COMPUTER01</value>
    <value>127</value>
    <value>106</value>
    <value>84</value>
</row>
</root>

这里的问题是,这只是我的收藏品之一。还有其他集合项,它们具有相同的格式,但可能不同的列数,因此我不能将列名硬编码到select语句中。此外,一些值可以为空。 其他收集项目的示例可以在下面找到:

<data>
    <head>
        <value>FriendlyName</value>
        <value>DnsNameList</value>
        <value>NotAfter</value>
        <value>NotBefore</value>
        <value>Thumbprint</value>
        <value>Issuer</value>
    </head>
    <row>
        <value />
        <value />
        <value>08/23/2021 07:59:59</value>
        <value>08/23/2011 08:00:00</value>
        <value>E1A7D7E3BD6CA0C832182248EF7F092A72F9EB67</value>
        <value>CN=Hewlett-Packard Private Class 2 Certification Authority, O=Hewlett-Packard Company, C=US, OU=IT Infrastructure, O=hp.com</value>
    </row>
    <row>
        <value>Microsoft Root Certificate Authority</value>
        <value />
        <value>05/10/2021 07:28:13</value>
        <value>05/10/2001 07:19:22</value>
        <value>CDD4EEAE6000AC7F40C3802C171E30148030C072</value>
        <value>CN=Microsoft Root Certificate Authority, DC=microsoft, DC=com</value>
    </row>
    <row>
        <value>Thawte Timestamping CA</value>
        <value />
        <value>01/01/2021 07:59:59</value>
        <value>01/01/1997 08:00:00</value>
        <value>BE36A4562FB2EE05DBB3D32323ADF445084ED656</value>
        <value>CN=Thawte Timestamping CA, OU=Thawte Certification, O=Thawte, L=Durbanville, S=Western Cape, C=ZA</value>
    </row>
    <row>
        <value>Microsoft Root Authority</value>
        <value />
        <value>12/31/2020 15:00:00</value>
        <value>01/10/1997 15:00:00</value>
        <value>A43489159A520F0D93D032CCAF37E7FE20A8B419</value>
        <value>CN=Microsoft Root Authority, OU=Microsoft Corporation, OU=Copyright (c) 1997 Microsoft Corp.</value>
    </row>
</data>

我需要能够将具有此结构的数据解析为SQL表,以便将来能够使用它。预期的结果将是SQL表,看起来像这样:

DeviceID VolumeName      SizeGB FreeSpaceGB FreeSpacePercent
-------- --------------- ------ ----------- ----------------
C:       c$OS-COMPUTER01 126    43          34
D:       D$DB-COMPUTER01 127    104         82
E:       E$COMPUTER01    127    106         84

目前,我将头数据和行数据解析为临时表,并迭代构建动态SQL代码的各个表,然后执行它以创建另一个表并将解析后的数据插入其中。

--code snippet which parses the rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) n, xx.value('.', 'VARCHAR(MAX)') v INTO #rows
FROM (values(@x)) t1(x)
CROSS APPLY x.nodes('//row/value') t2(xx)

...

--code snippet building the INSERT queries
WHILE (@loopcounter * @columns) < @rows
BEGIN
    DECLARE @insertstatement VARCHAR(MAX)
    SET @insertstatement = 'INSERT INTO #result VALUES ('
    DECLARE @forcounter INT = 1
    WHILE @forcounter <= @columns
    BEGIN
        DECLARE @rownumber int = (@loopcounter * @columns) + @forcounter
        SELECT @insertstatement += '''' + REPLACE(v, '''', '''''') + ''', ' FROM #rows WHERE n = @rownumber

        SET @forcounter += 1
    END     
    SET @insertstatement = SUBSTRING(@insertstatement, 1, LEN(@insertstatement) - 1)
    SET @insertstatement += ')'
    EXEC (@insertstatement)
    SET @loopcounter += 1   
END

...

我搜索了这个论坛,但还没找到解决方案,它可以动态地使用以这种方式存储的列名。你能告诉我们可以做些什么吗?

1 个答案:

答案 0 :(得分:1)

以下查询将以非透视格式检索所有数据:

DECLARE @xml XML=
'<root>
<head>
    <value>DeviceID</value>
    <value>VolumeName</value>
    <value>SizeGB</value>
    <value>FreeSpaceGB</value>
    <value>FreeSpacePercent</value>
</head>
<row>
    <value>C:</value>
    <value>c$OS-COMPUTER01</value>
    <value>126</value>
    <value>43</value>
    <value>34</value>
</row>
<row>
    <value>D:</value>
    <value>D$DB-COMPUTER01</value>
    <value>127</value>
    <value>104</value>
    <value>82</value>
</row>
<row>
    <value>E:</value>
    <value>E$COMPUTER01</value>
    <value>127</value>
    <value>106</value>
    <value>84</value>
</row>
</root>';

- 查询

WITH ColumnNames AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS ColumnInx
          ,c.value(N'text()[1]','nvarchar(max)') AS ColumnName
    FROM @xml.nodes(N'/root/head/value') AS A(c) 
)
,TheRows AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowInx
          ,r.query(N'value') AS TheValues
    FROM @xml.nodes(N'/root/row') AS B(r)
)
SELECT r.RowInx
      ,cn.ColumnInx
      ,cn.ColumnName
      ,r.TheValues.value(N'value[sql:column("ColumnInx")][1]','nvarchar(max)') AS row_value
FROM TheRows AS r
CROSS JOIN ColumnNames AS cn;

结果

+--------+-----------+------------------+-----------------+
| RowInx | ColumnInx | ColumnName       | row_value       |
+--------+-----------+------------------+-----------------+
| 1      | 1         | DeviceID         | C:              |
+--------+-----------+------------------+-----------------+
| 1      | 2         | VolumeName       | c$OS-COMPUTER01 |
+--------+-----------+------------------+-----------------+
| 1      | 3         | SizeGB           | 126             |
+--------+-----------+------------------+-----------------+
| 1      | 4         | FreeSpaceGB      | 43              |
+--------+-----------+------------------+-----------------+
| 1      | 5         | FreeSpacePercent | 34              |
+--------+-----------+------------------+-----------------+
| 2      | 1         | DeviceID         | D:              |
+--------+-----------+------------------+-----------------+
| 2      | 2         | VolumeName       | D$DB-COMPUTER01 |
+--------+-----------+------------------+-----------------+
| 2      | 3         | SizeGB           | 127             |
+--------+-----------+------------------+-----------------+
| 2      | 4         | FreeSpaceGB      | 104             |
+--------+-----------+------------------+-----------------+
| 2      | 5         | FreeSpacePercent | 82              |
+--------+-----------+------------------+-----------------+
| 3      | 1         | DeviceID         | E:              |
+--------+-----------+------------------+-----------------+
| 3      | 2         | VolumeName       | E$COMPUTER01    |
+--------+-----------+------------------+-----------------+
| 3      | 3         | SizeGB           | 127             |
+--------+-----------+------------------+-----------------+
| 3      | 4         | FreeSpaceGB      | 106             |
+--------+-----------+------------------+-----------------+
| 3      | 5         | FreeSpacePercent | 84              |
+--------+-----------+------------------+-----------------+

如果您需要表格格式,则可以使用动态创建的PIVOT命令完成此操作。 Here is one example如何做到这一点。