Friday, October 7, 2011

SQL - OPENXML vs xQuery

--One of the nice ways to work with XML in SQL Server 2005/2008

DECLARE @idoc int
DECLARE @doc varchar(1000)
SET @doc ='
<ROOT>
<Customer CustomerID="VINET" ContactName="Paul Henriot">
<Order OrderID="10248" CustomerID="VINET" EmployeeID="5"
OrderDate="1996-07-04T00:00:00">
<OrderDetail ProductID="11" Quantity="12"/>
<OrderDetail ProductID="42" Quantity="10"/>
</Order>
</Customer>
<Customer CustomerID="LILAS" ContactName="Carlos Gonzlez">
<Order OrderID="10283" CustomerID="LILAS" EmployeeID="3"
OrderDate="1996-08-16T00:00:00">
<OrderDetail ProductID="72" Quantity="3"/>
</Order>
</Customer>
</ROOT>'
--Create an internal representation of the XML document.
EXEC sp_xml_preparedocument @idoc OUTPUT, @doc
-- SELECT stmt using OPENXML rowset provider
SELECT *
FROM OPENXML (@idoc, '/ROOT/Customer/Order/OrderDetail',2)
WITH (OrderID int '../@OrderID',
CustomerID varchar(10) '../@CustomerID',
OrderDate datetime '../@OrderDate',
ProdID int '@ProductID',
Qty int '@Quantity')


DECLARE @Result int
EXEC @Result = sp_xml_removedocument @idoc
IF @Result = 0
SET @idoc = NULL

------------------------------------------------------------------------------------------------------------
DECLARE @idoc int
DECLARE @doc varchar(1000)
SET @doc ='<Root>'+ (
SELECT
'Text Sample' AS TextCol,
GETDATE() AS DateCol,
12345 AS IntCol
FOR XML PATH('Sample')
)
SET @doc = @doc + '</Root>'
SELECT @doc = CAST(CAST(@doc AS XML) AS NVARCHAR(MAX))
EXEC sp_xml_preparedocument @idoc OUTPUT, @doc
SELECT *
FROM OPENXML (@idoc, '/Root/Sample', 2)
WITH
(
TextCol VARCHAR(50),
DateCol DATETIME,
IntCol INT,

xmldata XML '.'
)
--the xmldata can be used to insert individual details into an XML in a single column

DECLARE @Result int
EXEC @Result = sp_xml_removedocument @idoc
IF @Result = 0
SET @idoc = NULL

------------------------------------------------------------------------------------------------------------

/*
Note:


Do not forget to use sp_xml_removedocument after using sp_xml_preparedocument...
It has an impact on memory usage...

The OpenXML opens up a single file and allows you to use is as a data set. The xml.NODES syntax (xQuery) is designed to allow you to run queries against XML columns in a table. So - if you have kept some 100 xml documents in a single table as separate rows, you could run one query against ALL of them in on shot, and get the results back. Using OPENXML - you'd need a loop, some place to store the results of each run , etc

Hence OpenXML and xQuery should be chosen wisely depending on the scenario.
*/


------------------------------------------------------------------------------------------------------------
--Sample usage between OpenXML and xQuery
------------------------------------------------------------------------------------------------------------

DECLARE @InputXML XML
SET @InputXML =
'<contacts>
<contact id="fce91f99-69d7-4a55-9ecf-000072f65eb2">
<name>name1</name>
<phone>123-432-1234</phone>
<email>1stmail@email.com</email>
</contact>

<contact id="0982747d-9089-49d8-b870-000072fe965a">
<name>name2</name>
<phone>567-123-1234</phone>
<email>2ndmail@email.com</email>
</contact>

<contact id="a5b4874e-538e-4349-94d3-00009cf28e07">
<name>Testing Name</name>
<phone>432-123-4213</phone>
<email>3rdmail@email.com</email>
</contact>
</contacts>'
--------------------------------------------------------
/*Using OpenXML*/
--------------------------------------------------------
DECLARE @XMLHandle INTEGER
DECLARE @Result INTEGER
EXEC sp_xml_preparedocument @XMLHandle OUTPUT, @InputXML
SELECT ID
,Name
,Phone
,EMail
,'Using OpenXML' AS Source
FROM OPENXML (@XMLHandle, '/contacts/contact', 2)
WITH ( id UNIQUEIDENTIFIER '@id'
,name VARCHAR(50)
,phone VARCHAR(50)
,email VARCHAR(50)
)
EXEC @Result = sp_xml_removedocument @XMLHandle
IF @Result = 0
SET @XMLHandle = NULL
--------------------------------------------------------
 
--------------------------------------------------------
/*Using xQuery*/
--------------------------------------------------------
SELECT Con.value('(@id)','UNIQUEIDENTIFIER') AS ID

,Con.value('name[1]','VARCHAR(50)') AS Name
,Con.value('phone[1]','VARCHAR(50)') AS Phone
,Con.value('email[1]','VARCHAR(50)') AS EMail
,'Using xQuery' AS Source
FROM @InputXML.nodes('/contacts/contact') AS x(Con)
--------------------------------------------------------