Pages

Wednesday, 10 August 2016

XML Methods

XML Methods (SQL Server 2008)

You can use the xml data type methods to query an XML instance stored in a variable or column of xml type.

The retrieving XML data is similar to retrieving objects configured with other data types. However, that’s true only when returning the entire XML value. If instead you want to work with individual components within an XML instance, such as  when retrieving the value of a single attribute, you must use one of the five methods that available to the XML data type-query(), value(), exist(), nodes(), or modify().

THE QUERY () METHOD:
The query () method retrieves a subset of untyped XML from the target XML instance. It’s probably the simplest and most straightforward of the XML methods. You need only specify the database object, the method name, and an XQuery expression, as shown in the following syntax:

DbObject.query ('XQuery')

You’ll notice that the XQuery expression is entirely enclosed in single quotes and those are enclosed in parentheses. The trick with any XML method is to get the XQuery expression right. But at its simplest, the expression must define the path of the element you want to retrieve.

In the following example, I use the query() method to return data from the <StoreSurvey> element in both the typed and untyped columns:

SELECT
  Survey_untyped.query('/StoreSurvey') AS Info_untyped,
  Survey_typed.query('declare namespace
    ns="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey";
    /ns:StoreSurvey') AS Info_typed
FROM
  Stores;

Survey_untyped.query('/StoreSurvey')
For the untyped column, I specified the column name (Survey_untyped) followed by a period and then the method name (query). Within the parentheses and single quotes, I defined the XQuery expression (/StoreSurvey). That’s all there is to it. Because the <StoreSurvey> element is the root node, the entire XML document is returned.

The XQuery expression for the Survey_typed column
The XQuery expression for the Survey_typed column is a bit more complex. Because it is a typed column, the expression should be preceded by a namespace declaration. The namespace must be the same as the one referenced within the XML document stored in the typed column.

To declare a namespace, you specify the declare namespace keywords, followed by the alias name (in this case, ns). After the alias, you include an equal sign (=) and then the schema path and name. You then end the declaration with a semi-colon (;) to separate it from the main body of the XQuery expression. After you declare the namespace, you can use the alias within the expression to reference that namespace.

The expression itself is nearly identical to the one used for the untyped column, except that you must precede the element name with the namespace alias and a colon (ns:). As with the untyped column, the expression will return the entire XML document because it specifies only the root node.

Although the preceding example is helpful in demonstrating the basics of using the query() method, it’s not much use beyond that because you can just as easily retrieve all the column contents simply by specifying the column name. However, in the following example, I get more specific by limiting the results to the <AnnualSales> child element:

SELECT
  Survey_untyped.query('/StoreSurvey/AnnualSales')
    AS Info_untyped,
  Survey_typed.query('declare namespace
    ns="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey";
    /ns:StoreSurvey/ns:AnnualSales') AS Info_typed
FROM
  Stores;

The only difference between this example and the preceding one is that I added /AnnualSales to the XQuery expression for the untyped column and /ns:AnnualSales to the expression for the typed column. The XQuery for the untyped column returns the following results.

<AnnualSales>800000</AnnualSales>

And the XQuery for the typed column returns these results:

<ns:AnnualSales xmlns:ns="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey">800000</ns:AnnualSales>

The primary difference between the two is that the data returned by the typed column includes the namespace information. The element values themselves (800000) are the same in both columns. If the <AnnualSales> element had included its own child elements, those too would have been displayed. For instance, the following example retrieves the <Products> elements, which are child elements of <StoreSurvey>, just like <AnnualSales>:

SELECT
  Survey_untyped.query('/StoreSurvey/Products')
    AS Products
FROM
  Stores;

Because the XML document includes two <Products> elements and those elements each include several <Product> child elements, the SELECT statement returns all product-related elements, as shown in the following results:

<Products Type="Bikes">
  <Product>Mountain</Product>
  <Product>Road</Product>
  <Product>Racing</Product>
</Products>
<Products Type="Clothes">
  <Product>jerseys</Product>
  <Product>jackets</Product>
  <Product>shorts</Product>
</Products>

If you want to return a specific element and its child elements, you can do so by referencing one of its attributes. For example, suppose you want to include only products from the Bikes category. To do so, you can modify your XQuery expression as follows:
SELECT
  Survey_untyped.query('/StoreSurvey/Products[@Type="Bikes"]')
    AS BikeProducts
FROM
  Stores;

Now the XQuery expression includes bracketed information that specifies the Type attribute and its value. The attribute name is preceded by the at (@) symbol, and the attribute value is preceded by an equal sign (=) and enclosed in double quotes. As a result, the SELECT statement now returns the following XML fragment:

<Products Type="Bikes">
  <Product>Mountain</Product>
  <Product>Road</Product>
  <Product>Racing</Product>
</Products>

As you would expect, only elements whose products are in the Bikes category are returned. You could have just as easily specified “Clothes” rather than “Bikes,” and you would have received the product elements related to clothing.




The VALUE () method
The value () method returns a scalar value from the targeted XML document. The returned value is converted to the data type you specify when you call the method. The value() method makes it easier to work with individual values in order to compare or combine them, either with other XML values or values of different types.
As the following syntax illustrates, when you call the value() method, you must specify the XQuery expression and the Transact-SQL data type for the returned data:
DbObject.value('XQuery', 'SqlType')
For example, suppose you want to pull the amount of sales from the <AnnualSales> element in either the typed or untyped column in the Stores table. You can specify the path in the XQuery expression plus a numerical data type as the second argument, as shown in the following SELECT statement:
SELECT
  Survey_untyped.value('(/StoreSurvey/AnnualSales)[1]', 'int')
    AS Sales_untyped,
  Survey_typed.value('declare namespace
    ns="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey";
    (/ns:StoreSurvey/ns:AnnualSales)[1]', 'int') AS Sales_typed
FROM
  Stores;

For the untyped column, the XQuery expression includes the element path as you saw in earlier examples. However, after the path, you must add an integer in brackets that indicates which element of that name you should retrieve. More often than not, you will simply add [1], as I’ve done here. This assures that, if there are multiple elements with the same name, only the first one will be returned. It also assures that only one element is being referenced at a time. In fact, even when there is only one element, as is the case with <AnnualSales>, you must still include the [1] because the value() method expects a singleton value.
The second argument passed into the value() method is the name of the data type, in this case, int. That means an int value will be returned by the method. Note that the method’s two arguments must each be enclosed in single quotes and separated with a comma.
As for the typed column, the namespace declaration and path are also similar to what you’ve seen in earlier examples. The only difference is that this expression must also include the [1], just as we did it for the untyped column. As a result, for each column, the SELECT statement returns a single int value of 800000, without any additional element information.
You can also retrieve an attribute value, rather than an element value, by specifying the name of the attribute in your element path. For example, the following SELECT statement retrieves the value of the Type attribute for the second instance of the <Products> element:
SELECT
  Survey_untyped.value('(/StoreSurvey/Products/@Type)[2]', 'varchar(10)')
    AS ProductType
FROM
  Stores; 
The expression includes [2], rather than [1], in order to retrieve data from the second instance of <Products>. As a result, the SELECT statement now returns the varchar value Clothes.
As stated earlier, the value() method is also handy if you want to combine or compare data. In the following example, I create a calculated column that’s based on two values returned from the untyped column:

SELECT
  Survey_untyped.value('(/StoreSurvey/AnnualSales)[1]', 'int') -
  Survey_untyped.value('(/StoreSurvey/AnnualRevenue)[1]', 'int')
    AS Expenses
FROM
  Stores; 
The first instance of value() retrieves the <AnnualSales> value. The second value() instance retrieves the<AnnualRevenue> value. The second value is then subtracted from the first value to return a scalar value of720000.
You can also use XQuery functions in your expressions. For instance, in the following example, I use the concat function to add a string to the <Specialty> value:
SELECT
  Survey_untyped.value('concat("Bike specialty: ",
   (/StoreSurvey/Specialty)[1])', 'varchar(25)')
   AS Specialty
FROM
  Stores; 
When you use the concat function, you specify each element that you want to concatenate as an argument to the function and separate those arguments with a comma. The statement returns the value Bike specialty: Mountain.
As you can see, the string has been concatenated with the <Specialty> value. I could have just as easily concatenated multiple element values or added more string values.

select Survey_untyped.value('(/StoreSurvey/Products)[1]','varchar(20)')
from stores

--------------------
MountainRoadRacing

select Survey_untyped.value('(/StoreSurvey/Products)[2]','varchar(20)')
from stores

--------------------
JerseysJacketsShorts

select Survey_untyped.value('(/StoreSurvey/Products[2]/Product)[1]','varchar(20)')
from stores

--------------------
Jerseys



The exist () method
The exist () method lets you test for the existence of an element or one of its values. The method takes only one argument, the XQuery expression, as shown in the following syntax:
DbObject.exist('XQuery')
The key to using the exist() method is in understanding the values it returns. Unlike the query() and value()methods, the exist() method doesn’t return XML content. Rather, the method returns one of the following three values:
·         A BIT value of 1 if the XQuery expression returns a nonempty result
·         A BIT value of 0 if the XQuery expression returns an empty result.
·         A NULL value if the XML data type instance is null.
A good way to test how the exist() method works is to use a variable to capture the method’s results, as I’ve done in the following example:
DECLARE @xml XML;
DECLARE @exist BIT;
SET @xml = (SELECT Survey_untyped FROM Stores);
SET @exist = @xml.exist('/StoreSurvey[BusinessType="BM"]');
SELECT @exist;
 First, I declared the @xml variable as type XML. Then I declared the @exist variable as type BIT. I set the value of@xml to equal the XML document in the Survey_untyped column. I then used the exist() method on the @xmlvariable to test for the existence of /StoreSurvey[BusinessType="BM"] within the XML document. In other words, the exist() methods checks whether the <BusinessType> child element exists and whether it contains a value of “BM”.
I then assigned the results returned by the exist() method to the @exist variable and used a SELECT statement to return the contents of the variable. Because the XML document contains this child element and that element has a value of “BM”, the method returns a value of 1. However, if I change “BM” to “BMX”, as I’ve done in the following example, the method returns 0 because the element and value don’t exist exactly as specified:
DECLARE @xml2 XML;
DECLARE @exist2 BIT;
SET @xml2 = (SELECT Survey_untyped FROM Stores);
SET @exist2 = @xml2.exist('/StoreSurvey[BusinessType="BMX"]');
SELECT @exist2;
Now that you have a sense of how the exist() method works, let’s look at how you might use it in your queries. In the following example, I include a WHERE clause that uses the exist() method to again test for the existence of/StoreSurvey[BusinessType="BM"]:
SELECT
  Survey_untyped.query('/StoreSurvey/Products[@Type="Bikes"]')
    AS BikeProducts
FROM
  Stores
WHERE
  Survey_untyped.exist('/StoreSurvey[BusinessType="BM"]') = 1;
The WHERE clause compares the results of the exist() method to the number 1. If the results equal 1, that is, if the XML contains the specified element and values, the WHERE clause evaluates to True and the SELECT statement returns the product information, as shown in the following results:
<Products Type="Bikes">
  <Product>Mountain</Product>
  <Product>Road</Product>
  <Product>Racing</Product>
</Products>
 If the exist() method had specified a different <BusinessType> value, as it does in the following example, theSELECT statement would instead return an empty result set:
SELECT
  Survey_untyped.query('/StoreSurvey/Products[@Type="Bikes"]')
    AS BikeProducts
FROM
  Stores
WHERE
  Survey_untyped.exist('/StoreSurvey[BusinessType="BMX"]') = 1;
 Again, the key to the exist() method is to remember that it returns only one of three values: 1, 0, or NULL.

DECLARE @xml2 XML;
DECLARE @exist2 BIT;
SET @xml2 = (SELECT Survey_untyped FROM Stores);
SET @exist2 = @xml2.exist('/StoreSurvey/Products[@Type = "Bikes"][Product= "Mountain"]');
SELECT @exist2;
O/P  :  1





The nodes() method
The nodes () method can be a bit more slippery to understand than the other XML methods. To begin with, rather than returning XML or scalar values, the nodes () method returns what is essentially a table that includes one column. That means you should use the method only in those parts of a statement that can handle rowset views, such as the FROM clause. It also means that, when you call the nodes () method, you must assign a table alias and column alias to the rowset view returned by the method, as shown in the following syntax:
DbObject.nodes('XQuery') AS TableAlias(ColumnAlias)
The table and column aliases let you reference the rowset view in other parts of the statement. The method is useful when you want to shred an XML document, that is, decompose the document into a relational format. To better understand how this works, let’s look at an example that uses the Nodes () method on an XML variable:
DECLARE @bikes XML
SET @bikes =
  '<Products>
    <Product>Mountain</Product>
    <Product>Road</Product>
    <Product>Racing</Product>
   </Products>'
SELECT
  Category.query('./text()')
    AS BikeTypes
FROM
  @bikes.nodes('/Products/Product')
    AS Bike(Category);

First, I declared the @bikes variable with the XML data type. Then I set its value to equal the XML fragment. In the FROM clause, I invoked the nodes() method on the variable to shred the <Product> elements. My goal was to retrieve the value from each instance of that element. I then provided a table alias (Bike) and a column alias (Category) to identify my rowset view. Now I essentially have a table with a single column that contains a row for each <Product> element.
When you use the nodes() method to create a rowset view of the XML data, you have to use one of the other XML methods to retrieve the contents of that view. In this case, I used the query() method in the SELECT list to return the value of each row. Notice that to retrieve the value I used a period to reference the current context node and the text() node function to retrieve only the element values, as shown in the following results:
Mountain
Road
Racing

If you want to use the nodes() method to retrieve data from a table, you can use the CROSS APPLY operator in the FROM clause to associate the table with the rowset view returned by the method, as shown in the following example:
SELECT
  Category.query('./text()')
    AS BikeTypes
FROM
  Stores CROSS APPLY
  Survey_untyped.nodes('/StoreSurvey/Products[@Type="Bikes"]/Product')
    AS Bike(Category);

In this case, I specified the XQuery path as StoreSurvey/Products[@Type="Bikes"]/Product' in order to return only the bike-related products from the Survey_untyped column. However the SELECT list itself is the same as the preceding example, so this statement returns the same results as that example.

The modify () method
Modifies the contents of an XML document. Use this method to modify the content of an xml type variable or column. This method takes an XML DML statement to insert, update, or delete nodes from XML data. The modify() method of the xml data type can only be used in the SET clause of an UPDATE statement.
Syntax:  modify (XML_DML)
XML_DML
Is a string in XML Data Manipulation Language (DML). The XML document is updated according to this expression.
An error is returned if the modify() method is called on a null value or results in a null value.

Adding Components in XML:
Inserts one or more nodes identified by Expression1 as child nodes or siblings of the node identified by Expression2.

Syntax
Insert
      Expression1 (
                 {as first | as last} into | after | before
                                    Expression2
                )

Arguments
Expression1
Identifies one or more nodes to insert.
This can be a constant XML instance;

A reference to a typed XML data type instance of the same XML Schema collection on which the modify method is being applied;

An untyped XML data type instance using a stand-alone sql: column()/sql:variable() function; or an XQuery expression. The expression can result in a node, and also a text node, or in an ordered sequence of nodes. It cannot resolve to the root (/) node.

If the expression results in a value or a sequence of values, the values are inserted as a single text node with a space separating each value in the sequence. If you specify multiple nodes as constant, the nodes are included in parentheses and are separated by commas. You cannot insert heterogeneous sequences such as a sequence of elements, attributes, or values. If Expression1 resolves to an empty sequence, no insertion occurs and no errors are returned.

into
Nodes identified by Expression1 are inserted as direct descendents (child nodes) of the node identified by Expression2. If the node in Expression2 already has one or more child nodes, you must use either as first or as last to specify where you want the new node added. For example, at the start or at the end of the child list, respectively. The as first and as last keywords are ignored when attributes are inserted.

After
Nodes identified by Expression1 are inserted as siblings directly after the node identified by Expression2. The after keyword cannot be used to insert attributes. For example, it cannot be used to insert an attribute constructor or to return an attribute from an XQuery.

Before
Nodes identified by Expression1 are inserted as siblings directly before the node identified by Expression2. The before keyword cannot be used when attributes are being inserted. For example, it cannot be used to insert an attribute constructor or to return an attribute from an XQuery.


Expression2
Identifies a node. The nodes identified in Expression1 are inserted relative to the node identified by Expression2. This can be an XQuery expression that returns a reference to a node that exists in the currently referenced document. If more than one node is returned, the insert fails. If Expression2 returns an empty sequence, no insertion occurs and no errors are returned. If Expression2 is statically not a singleton, a static error is returned. Expression2 cannot be a processing instruction, comment, or attribute. Note that Expression2 must be a reference to an existing node in the document and not a constructed node.




Examples
A. Inserting element nodes into the document
USE AdventureWorks2008; 
GO 
DECLARE @myDoc xml;        
SET @myDoc =
'<Root>        
    <ProductDescription ProductID="1" ProductName="Road Bike">        
        <Features>        
        </Features>        
    </ProductDescription>        
</Root>'  ;      

SELECT @myDoc;    

-- insert first feature child (no need to specify as first or as last)        
SET @myDoc.modify('        
insert <Maintenance>3 year parts and labor extended maintenance is available</Maintenance>  
into (/Root/ProductDescription/Features)[1]') ; 
SELECT @myDoc ;       

-- insert second feature. We want this to be the first in sequence so use 'as first'        
set @myDoc.modify('        
insert <Warranty>1 year parts and labor</Warranty>         
as first        
into (/Root/ProductDescription/Features)[1]')  ;      
SELECT @myDoc  ;      

-- insert third feature child. This one is the last child of <Features> so use 'as last'        
SELECT @myDoc        
SET @myDoc.modify('        
insert <Material>Aluminium</Material>         
as last
into (/Root/ProductDescription/Features)[1]')        
SELECT @myDoc ;       

-- Add fourth feature - this time as a sibling (and not a child)        
-- 'after' keyword is used (instead of as first or as last child)        
SELECT @myDoc  ;      
set @myDoc.modify('        
insert <BikeFrame>Strong long lasting</BikeFrame>  
after (/Root/ProductDescription/Features/Material)[1]')  ;      
SELECT @myDoc; 

-- Add fourth feature - this time as a sibling (and not a child)        
-- 'before' keyword is used (instead of as first or as last child)        
SELECT @myDoc  ;      
set @myDoc.modify('        
insert <BikeColor>Red</BikeColor>
before (/Root/ProductDescription/Features/BikeFrame)[1]')  ;      
SELECT @myDoc; 

GO

<Root>
  <ProductDescription ProductID="1" ProductName="Road Bike">
    <Features>
      <Warranty>1 year parts and labor</Warranty>
      <Maintenance>3 year parts and labor extended maintenance is available</Maintenance>
      <Material>Aluminium</Material>
      <BikeColor>Red</BikeColor>
      <BikeFrame>Strong long lasting</BikeFrame>
    </Features>
  </ProductDescription>
</Root>

Inserting multiple elements into the document


USE AdventureWorks; 
GO 
DECLARE @myDoc xml; 
SET @myDoc = N'<Root>            
<ProductDescription ProductID="1" ProductName="Road Bike">            
    <Features> </Features>            
</ProductDescription>            
</Root>'; 
DECLARE @newFeatures xml; 
SET @newFeatures = N'<Warranty>1 year parts and labor</Warranty>           
<Maintenance>3 year parts and labor extended maintenance is available</Maintenance>';          

-- insert new features from specified variable           
SET @myDoc.modify('            
insert sql:variable("@newFeatures")            
into (/Root/ProductDescription/Features)[1] ')            
SELECT @myDoc; 
GO

<Root>
  <ProductDescription ProductID="1" ProductName="Road Bike">
    <Features>
      <Warranty>1 year parts and labor</Warranty>
      <Maintenance>3 year parts and labor extended maintenance is available</Maintenance>
    </Features>
  </ProductDescription>
</Root>




Inserting attributes into a document

USE AdventureWorks2008; 
GO 
DECLARE @myDoc xml ;           
SET @myDoc =  
'<Root>            
    <Location LocationID="10" >            
        <step>Manufacturing step 1 at this work center</step>            
        <step>Manufacturing step 2 at this work center</step>            
    </Location>            
</Root>' ;  
SELECT @myDoc  ;         

-- insert LaborHours attribute            
SET @myDoc.modify('            
insert attribute LaborHours {".5" }            
into (/Root/Location[@LocationID=10])[1] ') ;          
SELECT @myDoc  ;         

-- insert MachineHours attribute but its value is retrived from a sql variable @Hrs            
DECLARE @Hrs float ;           
SET @Hrs =.2   ;         
SET @myDoc.modify('            
insert attribute MachineHours {sql:variable("@Hrs") }            
into   (/Root/Location[@LocationID=10])[1] ');           
SELECT @myDoc;            

-- insert sequence of attribute nodes (note the use of ',' and ()             
-- around the attributes.            
SET @myDoc.modify('            
insert (              
           attribute SetupHours {".5" },            
           attribute SomeOtherAtt {".2"}            
        )            
into (/Root/Location[@LocationID=10])[1] ');            
SELECT @myDoc; 
GO 


<Root>
  <Location LocationID="10" LaborHours=".5" MachineHours="0.2" SetupHours=".5" SomeOtherAtt=".2">
    <step>Manufacturing step 1 at this work center</step>
    <step>Manufacturing step 2 at this work center</step>
  </Location>
</Root>









 







Inserting a comment node

USE AdventureWorks2008; 
GO 
DECLARE @myDoc xml;            
SET @myDoc =  
'<Root>            
    <Location LocationID="10" >            
        <step>Manufacturing step 1 at this work center</step>            
        <step>Manufacturing step 2 at this work center</step>            
    </Location>            
</Root>' ;          
SELECT @myDoc;            
SET @myDoc.modify('            
insert <!-- some comment -->            
after (/Root/Location[@LocationID=10]/step[1])[1] ');           
SELECT @myDoc; 
GO 

<Root>
  <Location LocationID="10">
    <step>Manufacturing step 1 at this work center</step>
    <!-- some comment -->
    <step>Manufacturing step 2 at this work center</step>
  </Location>
</Root>


Inserting a processing instruction


USE AdventureWorks2008;
GO
DECLARE @myDoc xml;
SET @myDoc =
'<Root>
  <Location LocationID="10" >
    <step>Manufacturing step 1 at this work center</step>
    <step>Manufacturing step 2 at this work center</step>
  </Location>
</Root>' ;
SELECT @myDoc ;
SET @myDoc.modify('
insert <?Program = "Instructions.exe" ?>
before (/Root)[1] ') ;
SELECT @myDoc ;
GO

<?Program = "Instructions.exe" ?>
<Root>
  <Location LocationID="10">
    <step>Manufacturing step 1 at this work center</step>
    <step>Manufacturing step 2 at this work center</step>
  </Location>
</Root>









Inserting data using a CDATA section
USE AdventureWorks; 
GO 
DECLARE @myDoc xml;            
SET @myDoc =  
'<Root>             
    <ProductDescription ProductID="1" ProductName="Road Bike">            
        <Features> </Features>            
    </ProductDescription>            
</Root>' ;           
SELECT @myDoc ;           
SET @myDoc.modify('            
insert <![CDATA[ <notxml> as text </notxml> or cdata ]]>  
into  (/Root/ProductDescription/Features)[1] ') ;  
SELECT @myDoc ; 
GO 

<Root>  
<ProductDescription ProductID="1" ProductName="Road Bike">  
<Features> <notxml> as text </notxml> or cdata </Features>  
</ProductDescription>  
</Root> 








Inserting text
USE AdventureWorks2008; 
GO 
DECLARE @myDoc xml; 
SET @myDoc = '<Root> 
<ProductDescription ProductID="1" ProductName="Road Bike"> 
<Features> 
 
</Features> 
</ProductDescription> 
</Root>' 
SELECT @myDoc; 
set @myDoc.modify(
 insert text{"Product Catalog Description"}  
 as first into (/Root)[1] 
'); 
SELECT @myDoc; 


<Root>Product Catalog Description<ProductDescription ProductID="1" ProductName="Road Bike"><Features /></ProductDescription></Root>





Inserting based on an if condition statement


USE AdventureWorks2008; 
GO 
DECLARE @myDoc xml; 
SET @myDoc =  
'<Root> 
    <Location LocationID="10" LaborHours="1.2" > 
        <step>Manufacturing step 1 at this work center</step> 
    <step>Manufacturing step 2 at this work center</step> 
    </Location> 
</Root>'; 
SELECT @myDoc 
SET @myDoc.modify(
insert 
if (/Root/Location[@LocationID=10]) 
then attribute MachineHours {".5"} 
else () 
    as first into   (/Root/Location[@LocationID=10])[1] '); 
SELECT @myDoc; 
GO 

<Root>
  <Location LocationID="10" LaborHours="1.2" MachineHours=".5">
    <step>Manufacturing step 1 at this work center</step>
    <step>Manufacturing step 2 at this work center</step>
  </Location>
</Root>





Updating components in XML data -- replace value of (XML DML)
Updates the value of a node in the document.

replace value of Expression1   
with Expression2
 
Expression1
Identifies a node whose value is to be updated. It must identify only a single node. That is, Expression1 must be a statical singleton. If the XML is typed, the type of the node must be a simple type. If multiple nodes are selected, an error is raised. If Expression1 returns an empty sequence, no value replacement occurs and no errors are returned. Expression1 must return a single element that has simply typed content (list or atomic types), a text node, or an attribute node. Expression1 cannot be a union type, a complex type, a processing instruction, a document node, or a comment node. If it is, an error is returned.

Expression2
Identifies the new value of the node. This can be an expression that returns a simply typed node, because data() will be used implicitly. If the value is a list of values, the update statement replaces the old value with the list. In modifying a typed XML instance, Expression2 must be the same type or a subtype of Expression1. Otherwise, an error is returned. In modifying an untyped XML instance, Expression2 must be an expression that can be atomized. Otherwise, an error is returned.
 
Replacing values in an XML instance
DECLARE @myDoc xml; 
SET @myDoc = '<Root> 
<Location LocationID="10"  
            LaborHours="1.1" 
            MachineHours=".2" >Manufacturing steps are described here. 
<step>Manufacturing step 1 at this work center</step> 
<step>Manufacturing step 2 at this work center</step> 
</Location> 
</Root>'; 
SELECT @myDoc; 
 
-- update text in the first manufacturing step 
SET @myDoc.modify(
  replace value of (/Root/Location/step[1]/text())[1] 
  with     "new text describing the manu step" 
'); 
SELECT @myDoc; 
-- update attribute value 
SET @myDoc.modify(
  replace value of (/Root/Location/@LaborHours)[1] 
  with     "100.0"
'); 
SELECT @myDoc; 

      <Root>
        <Location LocationID="10" LaborHours="100.0" MachineHours=".2">Manufacturing steps are described here. 
      <step>new text describing the manu step</step><step>Manufacturing step 2 at this work center</step></Location>
      </Root>



Using the if expression to determine replacement value
DECLARE @myDoc xml 
SET @myDoc = '<Root> 
<Location LocationID="10"  
            LaborHours=".1" 
            MachineHours=".2" >Manu steps are described here. 
<step>Manufacturing step 1 at this work center</step> 
<step>Manufacturing step 2 at this work center</step> 
</Location> 
</Root>' 
--SELECT @myDoc 
 
SET @myDoc.modify(
  replace value of (/Root/Location[1]/@LaborHours)[1] 
  with ( 
       if (count(/Root/Location[1]/step) > 3) then 
         "3.0" 
       else 
          "1.0" 
      ) 
') 
SELECT @myDoc 


<Root>
  <Location LocationID="10" LaborHours="1.0" MachineHours=".2">Manu steps are described here. 
<step>Manufacturing step 1 at this work center</step><step>Manufacturing step 2 at this work center</step></Location></Root>
Updating XML stored in an untyped XML column
drop table
go 
CREATE TABLE T (i int, x xml) 
go 
INSERT INTO T VALUES(1,'<Root> 
<ProductDescription ProductID="1" ProductName="Road Bike"> 
<Features> 
  <Warranty>1 year parts and labor</Warranty> 
  <Maintenance>3 year parts and labor extended maintenance is available</Maintenance> 
</Features> 
</ProductDescription> 
</Root>') 
go 
-- verify the current <ProductDescription> element 
SELECT x.query(' /Root/ProductDescription') 
FROM
-- update the ProductName attribute value 
UPDATE
SET x.modify(
  replace value of (/Root/ProductDescription/@ProductName)[1] 
  with "New Road Bike" ') 
-- verify the update 
SELECT x.query(' /Root/ProductDescription') 
FROM

<ProductDescription ProductID="1" ProductName="New Road Bike">
  <Features>
    <Warranty>1 year parts and labor</Warranty>
    <Maintenance>3 year parts and labor extended maintenance is available</Maintenance>
  </Features>
</ProductDescription>
Deleting components from XML data
Deletes nodes from an XML instance.
Syntax: delete Expression 
Arguments
Expression
Is an XQuery expression identifying the nodes to be deleted. All the nodes selected by the expression, and also all the nodes or values that are contained within the selected nodes, are deleted. As described in insert (XML DML), this must be a reference to an existing node in the document. It cannot be a constructed node. The expression cannot be the root (/) node. If the expression returns an empty sequence, no deletion occurs and no errors are returned.

Deleting nodes from a document stored in an untyped xml variable

DECLARE @myDoc xml 
SET @myDoc = '<?Instructions for=TheWC.exe ?>  
<Root> 
 <!-- instructions for the 1st work center --> 
<Location LocationID="10"  
            LaborHours="1.1" 
            MachineHours=".2" >Some text 1 
<step>Manufacturing step 1 at this work center</step> 
<step>Manufacturing step 2 at this work center</step> 
</Location> 
</Root>' 
SELECT @myDoc  
 
-- delete an attribute 
SET @myDoc.modify(
  delete /Root/Location/@MachineHours 
') 
SELECT @myDoc 
 
-- delete an element 
SET @myDoc.modify(
  delete /Root/Location/step[2] 
') 
SELECT @myDoc 
 
-- delete text node (in <Location> 
SET @myDoc.modify(
  delete /Root/Location/text() 
') 
SELECT @myDoc 
 
-- delete all processing instructions 
SET @myDoc.modify(
  delete //processing-instruction() 
') 
SELECT @myDoc 

Final Output:
<Root>
  <!-- instructions for the 1st work center -->
  <Location LocationID="10" LaborHours="1.1">
    <step>Manufacturing step 1 at this work center</step>
  </Location>
</Root>




Deleting nodes from a document stored in an untyped xml column

CREATE TABLE T (i int, x xml) 
go 
INSERT INTO T VALUES(1,'<Root> 
<ProductDescription ProductID="1" ProductName="Road Bike"> 
<Features> 
  <Warranty>1 year parts and labor</Warranty> 
  <Maintenance>3 year parts and labor extended maintenance is available</Maintenance> 
</Features> 
</ProductDescription> 
</Root>') 
go 
-- verify the contents before delete 
SELECT x.query(' //ProductDescription/Features') 
FROM
-- delete the second feature 
UPDATE
SET x.modify('delete /Root/ProductDescription/Features/*[2]') 
-- verify the deletion 
SELECT x.query(' //ProductDescription/Features') 
FROM

Final Output:
<Features>
  <Warranty>1 year parts and labor</Warranty>

</Features>

No comments:

Post a Comment