Reputation: 111
I have code in SQL Server which extracts the tags from XML and stores them in a temp table (#result). Right now the cursor runs in a loop for each XML assuming that structure of all XMLs is same. Example below:
XML1 : <Root><Tag1>val1</Tag1><Tag2>val2</Tag2><Tag3>val3</Tag3></Root>
Tag1 | Tag2 | Tag3
--------|-----------|--------
val1 | val2 | val3
But now we have some XMLs which can have less number of tags. Example below:
XML1 : <Root><Tag1>val1</Tag1><Tag2>val2</Tag2><Tag3>val3</Tag3></Root>
XML2 : <Root><Tag1>val1</Tag1><Tag2>val2</Tag2><Tag3>val3</Tag3><Tag4>val4</Tag4></Root>
XML3 : <Root><Tag1>val1</Tag1><Tag2>val2</Tag2></Root>
Tag1 | Tag2 | Tag3 | Tag4
--------|-----------|-----------|--------
val1 | val2 | val3 |
--------|-----------|-----------|--------
val1 | val2 | val3 | val4
--------|-----------|-----------|--------
val1 | val2 | |
Below is my existing cursor code.
Open C_XML
Fetch next from C_XML into @input_xml
while @@FETCH_STATUS = 0
Begin
Create Table #MyTempTable (
name varchar(max),
value varchar(max)
);
insert into #MyTempTable
SELECT
bar.value('local-name(.)','VARCHAR(max)') as name,
bar.value('./.','VARCHAR(max)') as value
FROM
@input_xml.nodes('/Root/*') AS input_xml(bar)
DECLARE @name NVARCHAR(MAX) = ''
DECLARE @val NVARCHAR(MAX) = ''
Declare @Query NVARCHAR(MAX) = ''
SELECT @name += QUOTENAME(name)+ ','
FROM
(
SELECT name
FROM #MyTempTable
) AS ColName
-- remvoing last comma
SET @name = LEFT(@name, LEN(@name)-1)
SET @Query =
'SELECT * INTO ##temp2 FROM
(SELECT
name,
value
FROM
#MyTempTable
)
AS TempTable
PIVOT(
max(value)
FOR name IN (' + @name +')
) AS SPivotTable'
EXECUTE sp_executesql @Query
drop table #MyTempTable
select * from ##temp2
if (@count = 1 )
begin
select * into #result from ##temp2
end
else
begin
insert into #result
select * from ##temp2
end
drop table ##temp2
Set @count = @count + 1
Fetch next from C_XML into @input_xml
END
CLOSE C_XML;
DEALLOCATE C_XML;
Please suggest how I can enhance this code to make it dynamic for all type of XMLs as mentioned in above example. Currently its giving error:
#result
table definition is not correct.
Upvotes: 0
Views: 53
Reputation: 67311
If I get this correctly, you would have first to analyse each and any XML to find any existing element name. With this list of names you have to build a SELECT generically, covering any element name anywhere in your table...
I think this is the wrong approach...
Furthermore, there are very few cases, where a CURSOR is actually needed. In almost any case, if you think you'd need a cursor, you should rethink your approach.
I'd suggest first to create a classical EAV-list as a staging table:
--A mockup to simulate your issue
DECLARE @YourTable TABLE(ID INT IDENTITY, YourXml XML);
INSERT INTO @YourTable VALUES ('<Root><Tag1>val1</Tag1><Tag2>val2</Tag2><Tag3>val3</Tag3></Root>')
,('<Root><Tag1>val1</Tag1><Tag2>val2</Tag2><Tag3>val3</Tag3><Tag4>val4</Tag4></Root>')
,('<Root><Tag1>val1</Tag1><Tag2>val2</Tag2></Root>');
--a set-based approach to get every value without the need of a cursor
SELECT t.ID
,A.AnyNode.value('local-name(.)','nvarchar(max)') AS ElementName
,A.AnyNode.value('text()[1]','nvarchar(max)') AS ElementValue
FROM @YourTable t
CROSS APPLY t.YourXml.nodes('/Root/*') A(AnyNode);
With this result you can proceed with standard SQL. If needed, you can create your dynamic SQL from here...
Upvotes: 2