views:

13

answers:

1

Hi All,

Trying to understand why you can't use sql_variant for the value type when using the XML nodes function within SQL Server? I have a scenario where I will be dynamically parsing some XML input and being able to use the sql_variant would be a nice alternative to having to assign a datatype variable or doing a lookup on sys.columns.

Example:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[XmlSourceTable]') AND type in (N'U'))
DROP TABLE [dbo].[XmlSourceTable]
GO
CREATE TABLE [dbo].[XmlSourceTable](
    [RecordId] [int] IDENTITY(1,1) NOT NULL,
    [XmlData] [xml] NOT NULL,
PRIMARY KEY CLUSTERED 
(
    [RecordId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

DECLARE @XML XML ='<?xml version="1.0" ?>
<Root>
      <Person>
            <Name>Simon</Name>
            <Age>20</Age>
            <Skills>
                  <Skill>Cooking</Skill>
                  <Skill>Cleaning</Skill>
            </Skills>
      </Person>
      <Person>
            <Name>Peter</Name>
            <Age>21</Age>
            <Skills>
                  <Skill>Ironing</Skill>
            </Skills>
      </Person>
</Root>'

INSERT INTO XmlSourceTable(XmlData)
SELECT @XML

GO

SELECT * FROM XmlSourceTable
GO
SELECT
      pref.value('(Name/text())[1]', 'varchar(50)') as PersonName,
      pref.value('(Age/text())[1]', 'int') as PersonAge,
      pref.query('Skills') as PersonSkills
FROM  
      XmlSourceTable CROSS APPLY
      XmlData.nodes('/Root/Person') AS People(pref)

So Instead of:

SELECT * FROM XmlSourceTable
GO
SELECT
      pref.value('(Name/text())[1]', 'varchar(50)') as PersonName,
      pref.value('(Age/text())[1]', 'int') as PersonAge,
      pref.query('Skills') as PersonSkills
FROM  
      XmlSourceTable CROSS APPLY
      XmlData.nodes('/Root/Person') AS People(pref)

It would be nice to use:

SELECT * FROM XmlSourceTable
GO
SELECT
      pref.value('(Name/text())[1]', 'sql_variant') as PersonName,
      pref.value('(Age/text())[1]', 'sql_variantt') as PersonAge,
      pref.query('Skills') as PersonSkills
FROM  
      XmlSourceTable CROSS APPLY
      XmlData.nodes('/Root/Person') AS People(pref)

But I get this error: Msg 9500, Level 16, State 1, Line 1 The data type 'sql_variant)' used in the VALUE method is invalid.

Anyway to use sql_variant in the nodes function?

Thanks,

S

A: 

sql_variant is a meta type that stores in the value the actual type of the data (int, char, float, date etc). The problem is that XML expressions do not have a type: what is the type of an XPath expression result like ('Name/text())[1]? I understand you can argue that if the XML has a schema, one could deduce the xs schema type associated with that node/attribute/element, but I'd say that maybe you could deduce the type and you still left with the vast majority of schema-less XML documents out there in use... This is why the .value() XML method needs a type, so it can coerce the text found in the XML into a proper type.

One thing worth of notice is that most XML values can be extracted as VARCHAR, which would give the original XML text for that element/attribute. You could then convert the text to the appropriate column. But I think doing what you do right now (look up the column type, build the XPath/type dynamically with the proper type) is better.

Remus Rusanu
Thanks Remus....makes sense.
scarpacci