You can do this easily if the stored XML is well formed, as in the following example: CREATE TABLE NTextXml NTextXmlColumn ntext NULL GO INSERT NTextXml SELECT ‘ Lot's of Junk!.
Trang 1But this statement succeeds:
INSERT XmlExample SELECT ‘<root><foo/></root>’
Let’s say you manage the data for a company that has just upgraded from SQL Server 2000
to 2008 You already store all your XML inside ntextcolumns, and it’s time to convert
those columns to xml You can do this easily if the stored XML is well formed, as in the
following example:
CREATE TABLE NTextXml
(
NTextXmlColumn ntext NULL
)
GO
INSERT NTextXml
SELECT
‘<feedback_review>
<parts_order id=”106”>
<customer_comment>Lot's of Junk!</customer_comment>
</parts_order>
</feedback_review>’
GO
ALTER TABLE NTextXml
ALTER COLUMN NTextXmlColumn xml NULL
Next, you would like to ensure that all your XML validates against a schema To change the
column from typed to untyped XML by associating a schema, you execute the following:
ALTER TABLE NTextXml
ALTER COLUMN NTextXmlColumn xml
(DOCUMENT HumanResources.HRResumeSchemaCollection)
go
XML Validation: Declaration not found for element ‘feedback_review’.
Location: /*:feedback_review[1]
The statement has been terminated.
Notice the error generated The reason is that the tags used are not defined in the
schemas ofHRResumeSchemaCollection, so the XML does not validate, and theALTER
TABLEstatement fails What you really want is for the XML to validate against your own
schema, which is described in the next section
Using XML Schema Collections
In this section, you define a simple XML schema, add it to a new schema collection stored
on the server, and create a table where you can store instances of this schema You also
add a check constraint to ensure that the value of the ProductIdattribute of the XML’s
root node matches the value of the ProductIdcolumn, using the xmldata type value()
method (discussed later in this chapter, in the section, “The Built-in xmlData Type
Trang 2Methods”) The foreign key constraint you define on ProductIdalso serves to ensure that
bothProductIdvalues reference a primary key value in HumanResources.Product
The real-world concept behind this sample schema is that it defines groups of customer
feedback calls and subsequent corporate responses pertaining to different kinds of orders
Listing 47.14 shows the schema and table definition
LISTING 47.14 An XSD and Table for Modeling and Storing Customer Feedback Reviews
use AdventureWorks2008
go
CREATE XML SCHEMA COLLECTION Sales.FeedbackSchemaCollection AS
‘<?xml version=”1.0”?>
<xsd:schema
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
targetNamespace=”urn:www-samspublishing-com:examples:feedback_review_xsd”
xmlns=”urn:www-samspublishing-com:examples:feedback_review_xsd”
elementFormDefault=”qualified”
attributeFormDefault=”unqualified”>
<xsd:element name=”feedback_review” type=”feedbackReviewType”/>
<xsd:complexType name=”feedbackReviewType”>
<xsd:sequence minOccurs=”1” maxOccurs=”unbounded”>
<xsd:element name=”order” type=”orderType”/>
</xsd:sequence>
<xsd:attribute
name=”product_id”
type=”xsd:integer”
use=”optional”/>
</xsd:complexType>
<xsd:complexType name=”feedbackType” mixed=”true”>
<xsd:attribute name=”id” type=”xsd:integer” use=”required”/>
</xsd:complexType>
<xsd:complexType name=”orderType”>
<xsd:choice minOccurs=”0” maxOccurs=”unbounded”>
<xsd:element name=”customer_comment” type=”feedbackType”/>
<xsd:element name=”company_response” type=”feedbackType”/>
</xsd:choice>
<xsd:attribute name=”id” type=”xsd:integer” use=”required”/>
<xsd:attribute name=”type” use=”required”>
<xsd:simpleType>
<xsd:restriction base=”xsd:string”>
<xsd:enumeration value=”parts”/>
<xsd:enumeration value=”product”/>
<xsd:enumeration value=”service”/>
</xsd:restriction>
</xsd:simpleType>
Trang 3</xsd:attribute>
</xsd:complexType>
</xsd:schema>’
GO
CREATE FUNCTION Sales.fnCheckProductId
(
@FeedbackReviewXml xml
)
RETURNS int
AS
BEGIN
DECLARE @ProductId int
SELECT @ProductId = @FeedbackReviewXml.value(‘
declare namespace
fr=”urn:www-samspublishing-com:examples:feedback_review_xsd”;
/fr:feedback_review[1]/@product_id’, ‘int’) RETURN @ProductId
END
GO
CREATE TABLE Sales.FeedbackReview
(
FeedbackReviewId int IDENTITY(1, 1) NOT NULL PRIMARY KEY,
ProductId int NULL REFERENCES Production.Product,
FeedbackReviewXml xml (DOCUMENT Sales.FeedbackSchemaCollection) NOT NULL,
CONSTRAINT ProductIdMatches
CHECK (Sales.fnCheckProductId(FeedbackReviewXml) = ProductId)
)
GO
Having created the xmlcolumn, you can now insert valid, well-formed documents into
FeedbackReviewin the following manner:
INSERT Sales.FeedbackReview
SELECT
NULL,
‘<feedback_review
xmlns=”urn:www-samspublishing-com:examples:feedback_review_xsd”>
<order id=”353” type=”service”>
<customer_comment id=”131”>
You guys said you'd be here on Monday.
</customer_comment>
<company_response id=”242”>I said Wednesday!</company_response>
</order>
Trang 4</feedback_review>’
GO
(1 row(s) affected)
UsingINSERT, you can input XML into xmlcolumns as varchar,xml, or literal string data,
or you can insert the output of a subquery that returns these types
The syntax used to create an XML schema collection is simple and straightforward:
CREATE SCHEMA COLLECTION schema_collection_name AS schema
Theschemaparameter can be either a string (as shown), or a variable that contains the
text of the schema of typevarchar,nvarchar,varbinary,nvarbinary, or xml
Dropping a schema collection is just as easy:
DROP SCHEMA COLLECTION schema_collection_name
If you ever want to select your schema back out again, you simply call the system
func-tionxml_schema_namespace, as in the following example:
SELECT xml_schema_namespace(
‘Sales’,
‘FeedbackSchemaCollection’,
‘urn:www-samspublishing-com:examples:feedback_review_xsd’
)
To add additional schemas to the collection, you use ALTER XML SCHEMA COLLECTION:
ALTER XML SCHEMA COLLECTION
Sales.FeedbackSchemaCollection ADD another schema
To view some of the nodes in your stored XML schemas, you query
sys.xml_schema_collectionand its related catalog views Here’s an example:
use AdventureWorks2008
go
SELECT el.name, el.*, el.must_be_qualified
FROM sys.columns sc
JOIN sys.xml_schema_collections xs
ON sc.xml_collection_id = xs.xml_collection_id
JOIN sys.xml_schema_elements el
ON xs.xml_collection_id = el.xml_collection_id
WHERE sc.name = ‘FeedbackReviewXml’
Given the name of the table’s typed xmlcolumn (FeedbackReviewXml), you can find its
associated schema collection by querying the catalog views as follows:
SELECT
sc.name XmlColumnName,
Trang 5xs.name CollectionName,
ns.name Namespace
from sys.columns sc
JOIN sys.xml_schema_collections xs
ON sc.xml_collection_id = xs.xml_collection_id
JOIN sys.xml_schema_namespaces ns
ON ns.xml_collection_id = sc.xml_collection_id
WHERE sc.name = ‘FeedbackReviewXml’
go
XmlColumnName CollectionName Namespace
-FeedbackReviewXml FeedbackSchemaCollection
urn:www-samspublishing-com:examples:feedback_review_xsd
You can accomplish the same thing by using the Object Browser in SSMS by viewing the
properties of the xmlcolumn or by right-clicking the Modify menu choice on the table
object, as shown in Figure 47.1
You can control permissions on schema collections by using the standard ALTER,CONTROL,
TAKE OWNERSHIP,REFERENCES,VIEW DEFINITION, and EXECUTEsyntax Here’s an example:
GRANT ALTER ON XML SCHEMA COLLECTION::Sales.FeedbackSchemaCollection
TO some_login
FIGURE 47.1 Viewing the properties of an xml column in SSMS.
Trang 6There are a few unsupported XML schema features in schema collections Check the Books
Online article titled “Guidelines and Limitations of XML Schema Collections on the
Server” for the most up-to-date information Following are some notable limitations:
The XSD constraints key,keyref, and uniqueare not supported
XSDincludeandredefineare not supported
Lax validation is not supported
You can also manage XML schema collections using SSMS To do so, you open the Object
Browser and expand the main tree to the following node:
ServerName\Databases\AdventureWorks2008\Programmability\Types\XML Schema
Collections Then you right-click a schema collection to drop it or to add new schemas.
You can also easily script schemas out for review whenever needed Figure 47.2 shows the
expanded Object Browser tree
The Built-in xml Data Type Methods
Now that you know how to create and manage typed and untyped xmlcolumns, the next
step is to learn how to query and modify stored XML content Although SQL Server
supports only a subset of the XQuery 1.0 recommendation, you’ll soon see that it’s plenty
to get the job done
FIGURE 47.2 Using the Object Browser to manage XML schema collections
Trang 7Keep in mind that a mastery of XQuery is not a requirement for selecting out XML data;
you can just specify the name of the xmlcolumn to select all the data back at once
SQL Server provides five built-in methods on the xmldata type: query(),exists(),
value(),nodes(), and modify() These methods are appended to the name of the xml
column in question, using the ColumnName.MethodName([MethodParameters])syntax
These methods work on XML in the following ways:
query()—Evaluates an XQuery expression into a node list, allowing for reshaping of
the selected nodes Results in untyped XML
exists()—Performs a Boolean test to see whether the result of an XQuery
expres-sion is empty (no matching nodes) Returns 1(non-empty), or 0(empty)
value()—Extracts a single (that is, scalar) value from an XML node and casts it to a
SQL Server relational data type (for example, int,varchar)
nodes()—Uses an XQuery expression to decompose the XML input into a rowset;
this is similar to the effect of OPENXML
modify()—Alters the content of an XML document using the insert,replace value
of, and deleteXQuery functions
XQuery is a bit like T-SQL in that it uses similar SELECT-FROM-WHERE-ORDER BYsemantics
to find the required nodes It also bears a resemblance to writing foreachloops with
object iterators in a language such as C# It is unique in that it combines the navigational
power of XPath to locate nodes and (in the same expressions) allows for new XML
genera-tion on the fly, all in one tight syntax package built especially for processing XML
To use XQuery effectively, you need to have at least a rudimentary understanding of
XPath A great starting point is the World Wide Web Consortium’s (W3C’s) site, at www
w3.org/TR/xpath20/ The following subsections assume such basic knowledge
Selecting XML by Using query()
The job of query()is to retrieve XML nodes by using XQuery expressions The result of
query()is an instance of untyped xml It takes a single parameter, a string literal
contain-ing the XQuery code itself
NOTE
Like all the other four xmldata type methods (and unlike most other T-SQL keywords),
query()is case sensitive This is in keeping with the case sensitivity of XML itself
NOTE
The parameter to query()cannot be a variable; it must be a string literal This puts
something of a hold on dynamic XQuery expressions However, declared T-SQL variables
and column values are available for use in XQuery, using the functions
sql:variable()andsql:column()(described later in this chapter)
Trang 8Each XQuery query is broken into two distinct parts, separated by a semicolon The first
part is known as the prolog This is the place where any namespaces used in the XPath
expressions and selected nodes are declared The second part is known as the body, and
this is the place where XPath and XQuery expressions are evaluated
The following example declares the actnamespace in its query prolog and then selects
anyact:eMailnodes from Person.Person.AdditionalContactInfoin its body:
SELECT
AdditionalContactInfo.query(
‘
declare namespace
act=”http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes”;
//act:eMail
‘
)
FROM Person.Person
WHERE ContactId = 2
go
<act:eMail
xmlns:act=”http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes”>
<act:eMailAddress>Joe@xyz.com</act:eMailAddress>
<act:SpecialInstructions>
Dont send emails for urgent issues Use telephone instead.
</act:SpecialInstructions>
</act:eMail>
Note that as with FOR XML, the result of query()can sometimes be an XML fragment (or
an empty string) You can again use FOR XML RAW, ROOTto guarantee that this won’t
happen Listing 47.15 illustrates this use, as well as the WITH XMLNAMESPACESstatement
LISTING 47.15 UsingWITH XMLNAMESPACES with FOR XML and query()
WITH XMLNAMESPACES
(
‘http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes’
as act
)
SELECT
FirstName,
LastName,
AdditionalContactInfo.query(
‘
//act:eMail
‘
Trang 9)
FROM Person.Person
WHERE BusinessEntityID = 2
FOR XML RAW(‘ContactInfo’), ROOT(‘Contact’)
go
<Contact
xmlns:act=”http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ContactTypes”>
<ContactInfo FirstName=”Catherine” LastName=”Abel”>
<act:eMail xmlns:act=”http://schemas.microsoft.com/sqlserver/2004/07/
adventure-works/ContactTypes”>
<act:eMailAddress>Joe@xyz.com</act:eMailAddress>
<act:SpecialInstructions>
Dont send emails for urgent issues Use telephone instead.
</act:SpecialInstructions>
</act:eMail>
</ContactInfo>
</Contact>
You can use WITH XMLNAMESPACESto declare namespaces for use in subsequent SELECT
statements Using this statement makes it possible to omit the prolog from the query() It
also has the desirable side effect of adding the actnamespace declaration to the root
Contactnode in the resulting FOR XML RAWwrapper It’s a great keystroke saver and helps
keepxmldata type queries readable
In addition to selecting nodes with simple XPath expressions, you can usequery()to
specifyWHEREclause conditions on the selected nodes, iterate through the nodes using
for-eachsemantics, order the nodes differently than in the original document, and
return XML in any desired structure, based on the selection This type of processing is
known by its acronym FLWOR (pronounced flower), which stands forfor,let,where,
order by,return
TheforClause The forclause establishes a variable that is bound to a node list for the
purpose of iterating over each node In each iteration of the forloop, this bound variable
takes the value of the context node It may be optionally typed (using as
XML_Schema_TypeName)to a schema-declared type, and it is followed by the XPath used to
match the nodes to be selected The bound variable in the following example is$ContextNode:
SELECT Instructions.query(‘
declare default element namespace
“http://schemas.microsoft.com/sqlserver/2004/07/adventure
-works/ProductModelManuInstructions”;
for $ContextNode in //Location
return
<LotSize>
{$ContextNode/@LotSize}
</LotSize>
Trang 10‘) as Result
FROM Production.ProductModel
WHERE ProductModelID = 10
Go
<LotSize
xmlns=”http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions” LotSize=”100” />
This example also shows the use of thedeclare default element namespacestatement,
which allows the specified XPath expressions that follow it to omit any namespace prefixes
In place of an XPath expression, you can use the bound variable to iterate through a
sequence of values, rather than nodes, as in the following example:
SELECT Instructions.query(‘
for $ContextNode in (1, 2, 3)
return
<Number>
{$ContextNode }
</Number>
‘) as Result
FROM Production.ProductModel
WHERE ProductModelID = 10
go
<Number>1</Number>
<Number>2</Number>
<Number>3</Number>
You can also specify more than one bound variable in the forclause Bound variables
subsequent to the first can be used in XPath queries against the first In this manner, two
related context nodes—one inner and one outer—can be created simultaneously This is
analogous to writing a nested forloop in a programming language, but here you need
only declare both context variables by using a comma Here’s an example:
DECLARE @Xml xml
SET @Xml = ‘
<outernode name=”a”>
<innernode>1</innernode>
<innernode>2</innernode>
<innernode>3</innernode>
</outernode>
<outernode name=”b”>
<innernode>4</innernode>
<innernode>5</innernode>
<innernode>6</innernode>
</outernode>
‘
SELECT @Xml.query(‘