ptg 1864 CHAPTER 46 SQLCLR: Developing SQL Server Objects in .NET ORDER BY assembly_id desc go name assembly_id permission_set SQLCLR 65719 UNSAFE_ACCESS System.Configuration.Install 65705 UNSAFE_ACCESS System.ServiceProcess 65704 UNSAFE_ACCESS System.Web.RegularExpressions 65703 UNSAFE_ACCESS System.Drawing.Design 65702 UNSAFE_ACCESS Now that you have the assembly_id for your SQLCLR project (yours will not be the same value as shown here), you can look up its routines and classes in sys.assembly_modules: SELECT TOP 5 name, assembly_class as class, assembly_method as method FROM sys.assembly_modules am JOIN sys.assemblies a ON am.assembly_id = a.assembly_id WHERE a.assembly_id = 65719 GO name class method SQLCLR StoredProcedures GetSetIllustrationWebLinks SQLCLR StoredProcedures SpTrans SQLCLR RegexLibrary MatchAll SQLCLR XSLT XSLTransform SQLCLR SumPrime NULL Notice that the class holding your UDA (SumPrime) is listed, but your UDA itself is not listed. In addition, your UDT ( RegexPattern) is not listed. To see everything, you right- click SQLCLR in the Assemblies node of the Object Browser and then select View Dependencies. Summary This chapter covered the development of SQLCLR code using user-defined managed data- base objects in conjunction with ADO.NET. We also reviewed advanced topics, such as transaction control and system catalog viewing. If you attempted the examples from start to finish, you have upgraded your SQL Server and .NET programming arsenal and opened the doorway to a new world of data-inte- grated software. In Chapter 47, “Using XML in SQL Server 2008,” we cover SQL Server’s extensive support for XML. ptg CHAPTER 47 Using XML in SQL Server 2008 IN THIS CHAPTER . What’s New in Using XML in SQL Server 2008 . Understanding XML . Relational Data as XML: The FOR XML Modes . XML as Relational Data: Using OPENXML . Using the xml Data Type . Indexing and Full-Text Indexing of xml Columns SQL Server first planted its Extensible Markup Language (XML) roots with the introduction of the FOR XML and OPENXML keywords in SQL Server 2000, right around the time XML was growing in popularity as a markup format with seemingly unlimited uses. With the release of SQL Server 2008, the bar has clearly been raised for XML support in relational databases. What’s New in Using XML in SQL Server 2008 XML and relational data may exist side by side and are more interchangeable than ever, thanks to SQL Server’s XML-centric features, such as the xml data type, extensions to FOR XML, schema storage, content validation, indexing, XQuery support, and more. With the release of SQL Server 2008, the SQL Server team added support for the few XML features that were unavail- able in SQL Server 2005, including . Support for lax validation in XML schemas . Improved support for union and list types in XML schemas . Addition of the let clause in XQuery . Enhancements to XML Data Modification Language (DML) expressions The introduction of these features is both vital and timely because XML has become the standard for everything from ptg 1866 CHAPTER 47 Using XML in SQL Server 2008 traditional markup for publications to business-to-business data exchange, web services, application programming (with XAML), graphics display (with SVG), news syndication (with RSS), and the list goes on. But before digging into the world of SQL Server XML, let’s briefly look at what XML is. Understanding XML XML was first created as a solution to the complexity inherent in the Standard Generalized Markup Language (SGML), the granddaddy of all structured markup languages. What you may not know is that SGML actually contains the rules that define how to produce other markup languages, such as HTML. XML is just a subset or restriction of those rules, providing the specifications for producing markup for all kinds of content based on a few simple conventions. XML documents are either well formed, meaning they contain a single root element that contains every other element (or none), or valid, meaning they are well formed and all their elements adhere to all the constraints set forth in their associated Document Type Definition (DTD) or XML Schema Definition (XSD). An XML document that adheres to the constraints of a particular DTD or schema is known as an instance of that DTD or schema. In some cases, an XML document is referred to as a fragment, meaning it contains more than one root element and/or text-only nodes. XML documents are generally made up of elements (also called tags), attributes, instruc- tions to the applications that use the document (known as processing instructions), comments, and text. Despite their variation in kind, all these structures are commonly known as nodes. Keep in mind that there is no set list of predefined XML tags: they can be anything the XML specialist chooses. And just as HTML is considered to be an instance of SGML, the XML content model is also itself an instance of XML. XML’s only job is to provide the specification for how a document may be structured (or marked up). It contains no inherent information pertaining to data display or layout, content usage, or anything else. The pages that follow provide many examples of XML, both simple and complex. We tour the many ways SQL Server can produce, store, load, and transform XML. Relational Data As XML: The FOR XML Modes One of the most important uses of XML is to provide a way of describing and encapsulat- ing relational data. Doing so requires a mapping between two basic kinds of data struc- tures: sets and trees. The techniques shown in this section thus have a single goal: converting the columns and rows that make up the sets derived from any SELECT state- ment into XML trees. ptg 1867 Relational Data As XML: The FOR XML Modes 47 Note that before XML came along, selected result sets would most likely be exported to delimited text files for consumption by disparate systems. Today, most data interchange favors the use of XML. In response, developers have come to rely on XSL for Transformations (XSLT) as a companion skill for translating XML into HTML, PDF, RTF, or any other type of document. Let’s look at how the SELECT FOR XML syntax can automatically mark up relational data in a variety of ways. The simplest approach uses FOR XML RAW. RAW Mode When specified at the end of a SELECT statement, the keywords FOR XML RAW tell SQL Server to generate a one-XML-element-per-row structure. The FOR XML statement has a few options that change its output from the default of document fragments to well-formed documents with a slightly (compared to a few other FOR XML options) reshaped structure. This is its syntax: FOR XML RAW [ (’ElementName’) ] [ [ , BINARY BASE64 ] [ , TYPE ] [ , ROOT [ (’RootName’) ] ] [ , { XMLDATA | XMLSCHEMA [ (’TargetNameSpaceURI’) ]} ] [ , ELEMENTS [ XSINIL | ABSENT ] ] Listing 47.1 illustrates the XML generated by the no-option version of FOR XML RAW. (Note that all the code in this chapter relies on the AdventureWorks2008 sample database.) LISTING 47.1 A SELECT Statement That Uses FOR XML RAW with No Additional Modifiers SELECT Name, ListPrice, Color FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW go <row Name=”Chain” ListPrice=”20.2400” Color=”Silver” /> <row Name=”Chain Stays” ListPrice=”0.0000” /> <row Name=”Chainring” ListPrice=”0.0000” Color=”Black” /> <row Name=”Chainring Bolts” ListPrice=”0.0000” Color=”Silver” /> <row Name=”Chainring Nut” ListPrice=”0.0000” Color=”Silver” /> This kind of XML shape is known as attribute-centric XML because each column in the result set is mapped to an attribute rather than an element. Each row is mapped to an element named row, which holds these attributes. ptg 1868 CHAPTER 47 Using XML in SQL Server 2008 Listing 47.2 illustrates how the resultant XML can be changed into an element-centric shape, where each selected column is converted to an XML element simply through the addition of the ELEMENTS keyword to FOR XML RAW. LISTING 47.2 A SELECT Statement That Uses FOR XML RAW, ELEMENTS SELECT Name, ListPrice, Color FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW, ELEMENTS go <row> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> </row> <row> <Name>Chain Stays</Name> <ListPrice>0.0000</ListPrice> </row> <row> <Name>Chainring</Name> <ListPrice>0.0000</ListPrice> <Color>Black</Color> </row> <row> <Name>Chainring Bolts</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> </row> <row> <Name>Chainring Nut</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> </row> If the tag name row is undesirable, you can change the element name by simply adding a string-valued parameter, in parentheses, to the RAW keyword. Note that in contrast to FOR XML AUTO (discussed later in this chapter), in this case, alias- ing the Production.Product table has no effect on the output. Here’s an example: SELECT Name, ListPrice, Color FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ptg 1869 Relational Data As XML: The FOR XML Modes 47 ORDER BY Name FOR XML RAW(‘ChainElement’), ELEMENTS SQL Server 2008 also enables you to return NULL column values in generated XML. Previously, when a NULL column value was returned in the result set when using FOR XML, the null value was simply omitted from the XML: no attribute or element was generated at all. In SQL Server 2008, by specifying the keyword XSINIL after ELEMENTS, you can ensure that all null values are represented in the XML. Note how the xsi:nil=”true” attribute is produced for elements representing null column values. In addition, SQL Server automatically adds the XML schema namespace declaration to each node of the resulting fragment. This is required under the rules of XML because this fragment uses a Boolean attribute called nil, which is declared in the XML schema located at the specified URL. This, as well as the effect of the ELEMENTS keyword, is illustrated in Listing 47.3. LISTING 47.3 A SELECT Statement That Uses FOR XML RAW, ELEMENTS XSINIL SELECT TOP 1 Name, ListPrice, Color, Weight FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW(‘ChainElement’), ELEMENTS XSINIL go <ChainElement xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> <Weight xsi:nil=”true”/> </ChainElement> Note that the XML results in Listing 47.3 happen to produce a well-formed XML docu- ment only because a single row was selected: this one row acts as both the root of the document and its entire content. All other XML results (including all the previous listings) encapsulating two or more rows are actually just fragments. To easily change these XML fragments to well-formed documents, you can apply the ROOT keyword to add a root node to the output, as shown in Listing 47.4. LISTING 47.4 A SELECT Statement That Uses FOR XML RAW and the ROOT Keyword SELECT Name, ListPrice, Color, Weight FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW(‘ChainElement’), ELEMENTS XSINIL, ROOT(‘ChainDoc’) go ptg 1870 CHAPTER 47 Using XML in SQL Server 2008 <ChainDoc xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”> <ChainElement> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> <Weight xsi:nil=”true” /> </ChainElement> <ChainElement> <Name>Chain Stays</Name> <ListPrice>0.0000</ListPrice> <Color xsi:nil=”true” /> <Weight xsi:nil=”true” /> </ChainElement> <ChainElement> <Name>Chainring</Name> <ListPrice>0.0000</ListPrice> <Color>Black</Color> <Weight xsi:nil=”true” /> </ChainElement> <ChainElement> <Name>Chainring Bolts</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> <Weight xsi:nil=”true” /> </ChainElement> <ChainElement> <Name>Chainring Nut</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> <Weight xsi:nil=”true” /> </ChainElement> </ChainDoc> Users (or applications) on the receiving side of RAW-produced XML may also require an inline XML schema (XSD) or an inline XML-Data Reduced (XDR) schema. Note that inline XDR schemas are considered to be deprecated in this release. To produce these schemas, you add the XMLSCHEMA or XMLDATA keyword to the clause. The results are too long to be listed here, but to see how these schema types differ, compare the output of this: SELECT Name, ListPrice, Color, Weight FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW, ELEMENTS XSINIL, XMLDATA ptg 1871 Relational Data As XML: The FOR XML Modes 47 to the output of this: SELECT Name, ListPrice, Color, Weight FROM Production.Product [Product] WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW(‘ChainElement’), ELEMENTS XSINIL, ROOT(‘ChainDoc’), XMLSCHEMA (‘urn:www-samspublishing-com:examples’) XML schemas are discussed in further detail later in this chapter, in the section “Using XML Schema Collections.” NOTE The XMLDATA keyword is not permitted when ROOT is specified or when a tag name parameter has been passed to RAW (for example, RAW(‘ChainElement’)). Note also that XMLSCHEMA takes an optional string-valued parameter, allowing you to specify a value for the target namespace of the produced XML (for example, XMLSCHEMA (‘urn:www-samspublishing-com:examples)). Working with Binary Columns Even though XML is purely a text-based markup language, FOR XML still has the capability to generate XML that contains data selected from binary–data-typed columns, such as image, binary, and varbinary. To do this, SQL Server base-64 encodes the data, resulting in a long character string. To implement this in a query, you add joins from the table Production.Product to Production.ProductProductPhoto and then to Production.ProductPhoto, which contains the varbinary(max)–data-typed ThumbNailPhoto column. Then you add the keywords BINARY BASE64 to the FOR XML clause. Listing 47.5 illustrates this and also shows the schema generated by the XMLSCHEMA keyword. (Note that the base-64 character data is truncated in the listing for brevity with the character string { }.) LISTING 47.5 A SELECT Statement That Uses FOR XML RAW and the BINARY BASE64 Option SELECT TOP 1 Name, ListPrice, Color, Weight, ThumbNailPhoto FROM Production.Product [Product] JOIN Production.ProductProductPhoto PhotoJunction ON [Product].ProductId = PhotoJunction.ProductId JOIN Production.ProductPhoto Photo ON Photo.ProductPhotoId = PhotoJunction.ProductPhotoId WHERE Name LIKE ‘%Chain%’ ORDER BY Name FOR XML RAW(‘ChainElement’), ptg 1872 CHAPTER 47 Using XML in SQL Server 2008 ELEMENTS XSINIL, ROOT(‘ChainDoc’), XMLSCHEMA(‘urn:www-samspublishing-com:examples’), BINARY BASE64 go <ChainDoc xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”> <xsd:schema targetNamespace=”urn:www-samspublishing-com:examples” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:sqltypes=”http://schemas.microsoft.com/sqlserver/2004/sqltypes” elementFormDefault=”qualified”> <xsd:import namespace=http://schemas.microsoft.com/sqlserver/2004/sqltypes schemaLocation=”http://schemas.microsoft.com/sqlserver/2004/ sqltypes/sqltypes.xsd” /> <xsd:element name=”ChainElement”> <xsd:complexType> <xsd:sequence> <xsd:element name=”Name” nillable=”1”> <xsd:simpleType sqltypes:sqlTypeAlias=”[AdventureWorks2008].[dbo].[Name]”> <xsd:restriction base=”sqltypes:nvarchar” sqltypes:localeId=”1033” sqltypes:sqlCompareOptions=”IgnoreCase IgnoreKanaType IgnoreWidth” sqltypes:sqlSortId=”52”> <xsd:maxLength value=”50” /> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name=”ListPrice” type=”sqltypes:money” nillable=”1” /> <xsd:element name=”Color” nillable=”1”> <xsd:simpleType> <xsd:restriction base=”sqltypes:nvarchar” sqltypes:localeId=”1033” sqltypes:sqlCompareOptions=”IgnoreCase IgnoreKanaType IgnoreWidth” sqltypes:sqlSortId=”52”> <xsd:maxLength value=”15” /> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name=”Weight” nillable=”1”> <xsd:simpleType> <xsd:restriction base=”sqltypes:decimal”> <xsd:totalDigits value=”8” /> <xsd:fractionDigits value=”2” /> </xsd:restriction> </xsd:simpleType> </xsd:element> ptg 1873 Relational Data As XML: The FOR XML Modes 47 <xsd:element name=”ThumbNailPhoto” type=”sqltypes:varbinary” nillable=”1”/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> <ChainElement xmlns=”urn:www-samspublishing-com:examples”> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> <Weight xsi:nil=”true” /> <ThumbNailPhoto>R0lGODlhUAAxAPcAAKeamoyLj { }</ThumbNailPhoto> </ChainElement> </ChainDoc> AUTO Mode When RAW mode is not enough, FOR XML AUTO provides a few more ways to shape your XML output. Its usefulness derives from its capability to produce nested XML elements from rows derived by joining multiple tables, in contrast to the flat structure of RAW mode. The ROOT keyword introduced earlier also applies with AUTO mode, and it is good practice to continue to use it in your queries. Like RAW mode, AUTO mode produces attribute-centric XML by default, but you can change this by using the ELEMENTS keyword. XSINIL and XMLSCHEMA are also applicable here, having the same effect as with RAW mode. Listing 47.6 illustrates these points. LISTING 47.6 A SELECT Statement That Uses FOR XML AUTO, ELEMENTS XSINIL, ROOT SELECT Color, Offer.SpecialOfferId Id, Product.ProductId Id, Name, Description [Desc], Size FROM Sales.SpecialOffer Offer JOIN Sales.SpecialOfferProduct OP ON OP.SpecialOfferId = Offer.SpecialOfferId JOIN Production.Product Product ON Product.ProductId = OP.ProductId WHERE Name LIKE ‘Mountain Bike%’ FOR XML AUTO, ELEMENTS XSINIL, ROOT(‘MountainBikeSpecials’) go . targetNamespace=”urn:www-samspublishing-com:examples” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:sqltypes=”http://schemas .microsoft. com/sqlserver/2004/sqltypes” elementFormDefault=”qualified”> <xsd:import namespace=http://schemas .microsoft. com/sqlserver/2004/sqltypes schemaLocation=”http://schemas .microsoft. com/sqlserver/2004/ sqltypes/sqltypes.xsd” /> <xsd:element. upgraded your SQL Server and .NET programming arsenal and opened the doorway to a new world of data-inte- grated software. In Chapter 47, “Using XML in SQL Server 2008, ” we cover SQL Server s extensive support. nillable=”1”> <xsd:simpleType sqltypes:sqlTypeAlias=”[AdventureWorks2008].[dbo].[Name]”> <xsd:restriction base=”sqltypes:nvarchar” sqltypes:localeId=”1033” sqltypes:sqlCompareOptions=”IgnoreCase