2012-02-08 29 views
6

Estoy usando SQL Server 2008.
La tarea: tomar un archivo XML y analizarlo en una (n) tabla SQL.
El problema: el número de columnas y sus nombres variarán según el XML.¿Cómo puedo crear una tabla SQL a partir de un archivo XML que tendrá un número dinámico de nodos?

Aquí hay algo de código:

DECLARE @xmlData XML; 
SET @xmlData = '<root> 
    <item id="1"> 
    <item_number>IT23</item_number> 
    <title>Item number twenty-three</title> 
    <setting>5 to 20</setting> 
    <parameter>10 to 16</parameter> 
    </item> 
    <item id="2"> 
    <item_number>RJ12</item_number> 
    <title>Another item with a 12</title> 
    <setting>7 to 35</setting> 
    <parameter>1 to 34</parameter> 
    </item> 
    <item id="3"> 
    <item_number>LN90</item_number> 
    <title>LN with 90</title> 
    <setting>3 to 35</setting> 
    <parameter>9 to 50</parameter> 
    </item> 
</root>' 

Por ejemplo, usando el XML anterior, necesitaría una tabla de SQL devueltos que se vería así:

table snapshot

Así es como llegué lo anterior tabla:

DECLARE @idoc INT; 
    EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlData 

    SELECT * 
    FROM OPENXML (@idoc, '/root/item', 2) 
    WITH (item_number VARCHAR(100), 
     title VARCHAR(100), 
     setting VARCHAR(100), 
     parameter VARCHAR(100)) 

Ahora digamos que el XML ha cambiado donde cada nodo de elemento tenía un nuevo nodo secundario con el nombre 'new_node'. De este modo:

<root> 
    <item id="1"> 
    <item_number>IT23</item_number> 
    <title>Item number twenty-three</title> 
    <setting>5 to 20</setting> 
    <parameter>10 to 16</parameter> 
    <new_node>data</new_node> 
    </item> 
    <item id="2"> 
    <item_number>RJ12</item_number> 
    <title>Another item with a 12</title> 
    <setting>7 to 35</setting> 
    <parameter>1 to 34</parameter> 
    <new_node>goes</new_node> 
    </item> 
    <item id="3"> 
    <item_number>LN90</item_number> 
    <title>LN with 90</title> 
    <setting>3 to 35</setting> 
    <parameter>9 to 50</parameter> 
    <new_node>here</new_node> 
    </item> 
</root> 

debo cambiar mi código para incluir el nuevo nodo:

SELECT * 
    FROM OPENXML (@idoc, '/root/item', 2) 
    WITH (item_number VARCHAR(100), 
     title VARCHAR(100), 
     setting VARCHAR(100), 
     parameter VARCHAR(100), 
     new_node VARCHAR(100)) 

Para obtener esta tabla:

second table from XML

Así que el problema es que los nodos secundarios de 'elemento' variará.

¿Cómo puedo generar las mismas tablas sin especificar las columnas? ¿Hay algún otro enfoque que tener que usar OPENXML?

+1

Simplemente curioso, ¿por qué necesita crear una tabla SQL desde el XML, en lugar de simplemente almacenar el XML? ¿Qué harás con una tabla SQL con un conjunto desconocido de columnas que no podrías hacer con XML almacenado en un tipo de datos XML? –

+0

Esto es como preguntar, ¿cómo puedo construir una casa si no sé cuántas habitaciones necesito? –

+0

Michael Fredrickson: "Guardaré el XML, esto es solo un ejemplo simplificado, pero al final la tabla será devuelta a una página de código subyacente que generará una tabla HTML –

Respuesta

10

Con un número dinámico de columnas necesita SQL dinámico.

declare @XML xml = 
'<root> 
    <item id="1"> 
    <item_number>IT23</item_number> 
    <title>Item number twenty-three</title> 
    <setting>5 to 20</setting> 
    <parameter>10 to 16</parameter> 
    <new_node>data</new_node> 
    </item> 
    <item id="2"> 
    <item_number>RJ12</item_number> 
    <title>Another item with a 12</title> 
    <setting>7 to 35</setting> 
    <parameter>1 to 34</parameter> 
    <new_node>goes</new_node> 
    </item> 
    <item id="3"> 
    <item_number>LN90</item_number> 
    <title>LN with 90</title> 
    <setting>3 to 35</setting> 
    <parameter>9 to 50</parameter> 
    <new_node>here</new_node> 
    </item> 
</root>' 

declare @SQL nvarchar(max) = '' 
declare @Col nvarchar(max) = ', T.N.value(''[COLNAME][1]'', ''varchar(100)'') as [COLNAME]' 

select @SQL = @SQL + replace(@Col, '[COLNAME]', T.N.value('local-name(.)', 'sysname')) 
from @XML.nodes('/root/item[1]/*') as T(N) 

set @SQL = 'select '+stuff(@SQL, 1, 2, '')+' from @XML.nodes(''/root/item'') as T(N)' 

exec sp_executesql @SQL, N'@XML xml', @XML 
+2

Creo que este es uno de los mejores trozos de trucos TSQL/XML que he presenciado, y resolvió mi problema - bravo – MrTelly

1

mejora en la respuesta de Mikael:

Con un número dinámico de columnas que necesita SQL dinámico. Este código creará dinámicamente una instrucción de selección que admita un número desconocido de nodos, incluidos elementos con diferentes listas de nodos.

declare @XML xml = 
'<root> 
    <item id="1"> 
    <item_number>IT23</item_number> 
    <title>Item number twenty-three</title> 
    <setting>5 to 20</setting> 
    <parameter>10 to 16</parameter> 
    <new_node>data</new_node> 
    </item> 
    <item id="2"> 
    <item_number>RJ12</item_number> 
    <title>Another item with a 12</title> 
    <setting>7 to 35</setting> 
    <parameter>1 to 34</parameter> 
    <new_node>goes</new_node> 
    </item> 
    <item id="3"> 
    <item_number>LN90</item_number> 
    <title>LN with 90</title> 
    <setting>3 to 35</setting> 
    <parameter>9 to 50</parameter> 
    <new_node>here</new_node> 
    <unique_node>test</unique_node> 
    </item> 
</root>' 

--build an XML object with the unique list of nodes 
DECLARE @xmlcolumns XML; 
WITH Xml_CTE AS 
(
    SELECT 
     CAST('<' + node.value('fn:local-name(.)', 
      'varchar(100)') + '>' AS varchar(100)) 
     + CAST('</' + node.value('fn:local-name(.)', 
      'varchar(100)') + '>' AS varchar(100)) AS name 
    FROM @xml.nodes('/root/item/*') AS roots(node) 

) 

SELECT @xmlcolumns = (
SELECT CONVERT(XML,name) FROM (
SELECT DISTINCT name 
FROM Xml_CTE 
) a 
FOR XML PATH(''), ROOT('root') 
) 



declare @SQL nvarchar(max) = '' 
declare @Col nvarchar(max) = ', T.N.value(''[COLNAME][1]'', ''varchar(100)'') as [COLNAME]'   

--use the unique column list xml object to build the select statement 
select @SQL = @SQL + replace(@Col, '[COLNAME]', T.N.value('local-name(.)', 'sysname')) 
from @XMLcolumns.nodes('/root/*') as T(N)  

--build the entire query statement, using the original XML object as the data source 
set @SQL = 'select '+stuff(@SQL, 1, 2, '')+' from @XML.nodes(''/root/item'') as T(N)' 

exec sp_executesql @SQL, N'@XML xml', @XML; 
Cuestiones relacionadas