Figure 1
Terminology
Term
|
Description
|
XML schema
|
Define the characteristics (including ordering, data types, and relationships) of an XML document. XML schemas are either XSD schemas or XDR schemas
|
XSD schema
|
The W3C standard format for XML schemas
|
XDR schema
|
A standard for XML schemas (worked on by Microsoft), presumably to be superseded by XSD schemas
|
XPath query
|
Part of the W3C XML standard, XPath is a language for querying XML documents
|
XSLT
|
A W3C standard for transforming XML documents into other formats. Frequently used to transform XML to HTML
|
Mapping schema
|
An XSD or XDR schema with SQL annotations
|
SQL annotations
|
Used in a mapping schema to specify the relationships between the XML elements and a relational data store
|
SQL template
|
An XML document used to extract data from SQL Server 2000 as XML. Referred to simply as templates in this article, they use SQL query extensions or a mapping schema with an XPath query. Template may also specify an XSLT stylesheet to transform the XML
|
SQL query extension
|
These are extensions to the standard SQL SELECT statement, instructing the data provider to return the result as XML: SELECT * FROM Authors FOR XML AUTO
|
SQL updategrams
|
An XML document used to insert, update, and delete relational database information. Updategrams must specify a mapping schema
|
Figure 2
Example SQL Annotation
<xsd:schema id="persons" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
xmlns:sql="urn:schemas-microsoft-com:mapping-schema">
<xsd:annotation>
<xsd:appinfo>
<sql:relationship name="authors_titleauthor" parent="authors"
parent-key="au_id" child="titleauthor" child-key="au_id" />
<sql:relationship name="titleauthor_authors"
parent="titleauthor" parent-key="au_id" child="authors"
child-key="au_id" />
<sql:relationship name="titleauthor_titles"
parent="titleauthor" parent-key="title_id" child="titles"
child-key="title_id" />
<sql:relationship name="titles_titleauthor" parent="titles"
parent-key="title_id" child="titleauthor"
child-key="title_id" />
<sql:relationship name="titles_publishers" parent="titles"
parent-key="pub_id" child="publishers" child-key="pub_id" />
</xsd:appinfo>
</xsd:annotation>
<xsd:element name="person" sql:relation="authors" sql:key-
fields="au_id">
•••
</xsd:element>
</xsd:schema>
Figure 3
XSD-compliant Mapping Schema
<xsd:element name="person" sql:relation="authors" sql:key-fields="au_id">
<xsd:complexType>
<xsd:all>
<xsd:element name="social_security_number" sql:field="au_id" type="xsd:string" />
<xsd:element name="first_name" sql:field="au_fname" type="xsd:string" />
<xsd:element name="last_name" sql:field="au_lname" type="xsd:string" />
<xsd:element name="phone" sql:field="phone" type="xsd:string" />
<xsd:element name="address" sql:is-constant="1">
•••
</xsd:element>
<xsd:element name="contract" sql:field="contract" type="xsd:integer" />
<xsd:element name="books" sql:is-constant="1">
<xsd:complexType>
<xsd:all>
<xsd:element name="book" sql:relation="titles" sql:key-fields="title_id"
sql:relationship="authors_titleauthor titleauthor_titles">
<xsd:complexType>
<xsd:all>
<xsd:element name="title_id" sql:field="title_id" type="xsd:string" />
<xsd:element name="title" sql:field="title" type="xsd:string" />
<xsd:element name="type" sql:field="type" type="xsd:string"
minOccurs="0" />
<xsd:element name="publisher_id" sql:field="pub_id" type="xsd:string"
minOccurs="0" />
<xsd:element name="price" sql:field="price" type="xsd:double" minOccurs="0" />
<xsd:element name="advance" sql:field="advance" type="xsd:double"
minOccurs="0" />
<xsd:element name="royalty" sql:field="royalty" type="xsd:integer"
minOccurs="0" />
<xsd:element name="year_to_date_sales" sql:field="ytd_sales" type="xsd:integer"
minOccurs="0" />
<xsd:element name="notes" sql:field="notes" type="xsd:string" minOccurs="0" />
<xsd:element name="publication_date" sql:field="pubdate" type="xsd:date"
minOccurs="0" />
<xsd:element name="publisher" sql:relation="publishers" sql:key-fields="pub_id"
sql:relationship="titles_publishers">
•••
</xsd:element>
<xsd:element name="authors" sql:is-constant="1">
<xsd:complexType>
<xsd:all>
<xsd:element name="author" sql:relation="titleauthor"
sql:key-fields="au_id"
sql:relationship="titles_titleauthor">
•••
</xsd:element>
</xsd:all>
</xsd:complexType>
</xsd:element>
</xsd:all>
</xsd:complexType>
</xsd:element>
</xsd:all>
</xsd:complexType>
</xsd:element>
Figure 4
XML from SQL Server
<persons xmlns:sql="urn:schemas-microsoft-com:xml-sql">
<person>
<social_security_number>172-32-1176</social_security_number>
<first_name>NAME CHANGE</first_name>
<last_name>White</last_name>
<phone>408 496-7223</phone>
<contract>1</contract>
<address>
<street>10932 Bigge Rd.</street>
<city>Menlo Park</city>
<state>CA</state>
<zip_code>94025</zip_code>
</address>
<books>
<book>
<title_id>PS3333</title_id>
<title>Prolonged Data Deprivation: Four Case Studies
</title>
<type>psychology </type>
<publisher_id>0736</publisher_id>
<price>19.99</price>
<advance>2000</advance>
<royalty>10</royalty>
<year_to_date_sales>4072</year_to_date_sales>
<notes>What happens when the data runs dry? Searching evaluations
of information-shortage effects.</notes>
<publication_date>1991-06-12</publication_date>
<publisher>
...
</publisher>
<authors>
<author>
<social_security_number>172-32-1176
</social_security_number>
<title_id>PS3333</title_id>
<author_order>1</author_order>
<royalty_percentage>100</royalty_percentage>
<last_name>White</last_name>
<first_name>Johnson</first_name>
</author>
</authors>
</book>
</books>
</person>
<person>
...
</person>
<person>
...
</person>
<person>
...
</person>
Figure 5
Example Updategram
<persons xmlns:updg='urn:schemas-microsoft-com:xml-updategram'>
<updg:sync mapping-schema='persons.xsd'>
<updg:before>
<person>
<social_security_number>267-41-2394
</social_security_number>
</person>
</updg:before>
<updg:after>
<person>
<social_security_number>267-41-2394
</social_security_number>
<first_name>NAME CHANGE</first_name>
</person>
</updg:after>
</updg:sync>
</persons>
Figure 7
Sample Implementation Methods
Name
|
Data Format
|
Presentation Tool
|
Notes
|
ASP/ADO
|
ADO Recordset
|
ASP
|
The traditional approach. Get recordsets from the database and loop through them, outputting HTML
|
ManualXML/XSLT
|
ADO Recordset XML Document
|
XSLT
|
Get recordsets from the database. Convert to XML and apply XSLT to output HTML
|
DirectXML/XSLT
|
XML Document
|
XSLT
|
Get XML document directly from SQL Server 2000 using XSD schema. Apply XSLT to output HTML
|
DirectXML/.NET
|
XML Document
|
.NET XML Web Control
|
Get XML document directly from SQL Server 2000 using XSD schema. Use server control to perform XSLT transform
|
DirectXML/.NET
|
XML Document
|
.NET DataGrid Web Control
|
Get XML document directly from SQL Server 2000 using XSD schema. Use DataGrid to display the table. Note that this sample does not include the client-side DHTML
|
Figure 9
ShowAuthorsView
sub ShowAuthorsView()
Dim objCon, objRS1, objRS2, objRS3, strTitleId
Set objCon = Server.CreateObject("ADODB.Connection")
objCon.Open "Provider=SQLOLEDB;Initial Catalog=pubs;Data _
Source=(local)", "sa", ""
set objRS1 = Execute(objCon, "SELECT* FROM authors ORDER BY au_lname")
InsertAuthorHeader
while objRS1.EOF = false
InsertAuthorRow( objRS1 )
Response.Write "<tr><td colspan=4>"
'Get this authors books
Set objRS2 = Execute(objCon, "SELECT titles.* FROM titles,
titleauthor" & _
" WHERE au_id = '" & objRS1("au_id") & _
"' AND titles.title_id = titleauthor.title_id")
InsertBookHeader objRS1("au_id")
while objRS2.EOF = false
'Get this book's authors
strTitleId = objRS2("title_id")
Set objRS3 = Execute(objCon, _
"SELECT authors.au_id, au_fname, " & _
"au_lname, au_ord, royaltyper FROM authors, titleauthor"
"WHERE title_id = '" & strTitleId & _
"' AND authors.au_id = titleauthor.au_id" & _
" ORDER BY au_ord")
InsertBookRow objRS1, objRS2, objRS3
objRS2.MoveNext
wend
InsertBookFooter
Response.Write "</td></tr>"
objRS1.MoveNext
wend
InsertAuthorFooter
End Sub
Figure 10
Summary of Solution 1
Goal
|
Rank
|
Notes
|
Division of labor
|
Very poor
|
All the code written required expert knowledge of the data model and the nuances of ASP and HTML. No abstraction of the data model
|
One transaction, one place
|
Very poor
|
Requires 49 separate recordsets (and 49 round-trips to the database!) to get the data required for the view
|
Look to the future
|
Very poor
|
Would not port well to .NET. No opportunity to make use of the dataset in other applications. Tight coupling with the data model
|
Figure 12
GetXMLFromDB
function GetXMLFromDB()
Dim objCon, objRS1, objAuthorNode, objXML, objXMLBooks
Dim objBookNode, objXMLAuthors, strAuId, strTitleId
Set objCon = Server.CreateObject("ADODB.Connection")
objCon.Open "Provider=SQLOLEDB;Initial Catalog=pubs;Data
Source=(local)", _
"sa", ""
set objRS1 = Execute(objCon, "SELECT * FROM authors ORDER BY
au_lname")
set objXML = RecordsetToXMLDoc( objRS1, "author" )
for each objAuthorNode in objXML.selectNodes("authors/author")
'Get this authors books
strAuId = objAuthorNode.selectSingleNode("au_id").text
Set objRS1 = Execute(objCon, "SELECT titles.* FROM titles," &_
titleauthor WHERE titles.title_id = " &_
titleauthor.title_id AND au_id = strAuId & "'")
Set objXMLBooks = RecordsetToXMLDoc( objRS1, "book" )
for each objBookNode in objXMLBooks.selectNodes("books/book")
'Get this book's authors
strTitleId = objBookNode.selectSingleNode("title_id").text
Set objRS1 = Execute(objCon,
"SELECT authors.au_id, au_fname, " "au_lname, au_ord," & _
"royaltyper FROM authors, titleauthor " & _
"WHERE authors.au_id = titleauthor.au_id and " & _
"title_id = '" & strTitleId & "' ORDER BY au_ord")
Set objXMLAuthors = RecordsetToXMLDoc( objRS1, "author" )
'Add the authors to this book node
objBookNode.appendChild
objXMLAuthors.selectSingleNode("authors")
next
'Now add this books node, to the author node
objAuthorNode.appendChild objXMLBooks.selectSingleNode("books")
next
Set GetXMLFromDB = objXML
end function
Figure 13
ShowAuthorsView
sub ShowAuthorsView()
if Request("xmlonly") = "true" then
Response.Write "<a href=xmlbyhand.asp>View Sample Application
</a><br><br>"
Response.Write TransformXML(GetXMLFromDB(), "xsl/defaultss.xsl")
else
Response.Write "<a href=xmlbyhand.asp?xmlonly=true>View XML
</a><br><br>"
If Request("cache") = "off" then
Response.Write TransformXML( GetXMLFromDB(),
"xsl\authors2.xsl")
ElseIf Request("cache") = "clear" then
ClearCachedXSLTemplate( "xsl\authors2.xsl" )
Else
Response.Write TransformXML2( GetXMLFromDB(), _
GetCachedXSLTTemplate( "xsl\authors2.xsl") )
End If
end if
end sub
Figure 14
Summary of Solution 2
Goal
|
Rank
|
Notes
|
Division of labor
|
Good
|
It is plausible that different people could have written this code. One group writes GetXMLFromDB and the other writes the XSLT and ShowAuthorsView
|
One transaction, one place
|
Good
|
It still requires 49 separate recordsets (and 49 round-trips to the database!) to get the data required for the view, but eventually the data is available as one XML document
|
Look to the future
|
Good
|
Would port well to .NET. Dataset could be used in other applications and XSLT could be used in .NET. Dataset still has tight coupling with the data model because the element names are derived from the data model. Hierarchy of the document roughly reflects the relational model as well
|
Figure 16
GetXMLFromDB
function GetXMLFromDB( xsl, xpath )
Dim objStream, objCon, objCmd, strPath
strPath = Left(Server.MapPath("xmlbysql2000.asp"), _
Len(Server.MapPath("xmlbysql2000.asp"))-16) & "sqlxml/"
Set objStream = Server.CreateObject("ADODB.Stream")
Set objCmd = Server.CreateObject("ADODB.Command")
Set objCon = Server.CreateObject("ADODB.Connection")
objCon.Open _
"provider=SQLXMLOLEDB.2.0;data provider=SQLOLEDB;" & _
"data source=(local);initial catalog=pubs", "sa", ""
objCmd.ActiveConnection = objCon
objCmd.CommandText = _
"<persons xmlns:sql='urn:schemas-microsoft-com:xml-sql'> " & _
" <sql:xpath-query mapping-schema='persons.xsd'> " & _
xpath & "</sql:xpath-query></persons> "
objStream.Open
' You need the dialect if you are executing a template.
objCmd.Dialect = "{5d531cb2-e6ed-11d2-b252-00c04f681b71}"
objCmd.Properties("Output Stream").Value = objStream
objCmd.Properties("Base Path").Value = strPath
if Len(xsl) <> 0 Then
'specify path relative to 'Base Path'
objCmd.Properties("xsl").Value = "..\" & xsl
End If
objCmd.Properties("Output Encoding") = "utf-8"
on error resume next 'Errors are written out as XML to the stream
objCmd.Execute "", "", 1024 '1024 = adExecuteStream
on error goto 0
objStream.Position = 0
objStream.Charset = "utf-8"
GetXMLFromDB = objStream.ReadText(-1) ' -1 = adReadAll
end function
Figure 18
Summary of Solution 3
Goal
|
Rank
|
Notes
|
Division of labor
|
Excellent
|
One group would write the map-ping schema, based on detailed knowledge of the database. Another group would write the XSLT and ShowAuthorsView
|
One transaction, one place
|
Excellent
|
One trip to the database, and the dataset is available in one XML document
|
Look to the future
|
Good
|
Would port well to .NET. Dataset could be used in other applications, and XSLT could be used in .NET. Dataset is not tied to the data model as all element names are specified in the template (can be different from what's stored in the database). Complete control of the XML hierarchy is also available
|
Figure 20
Summary of Solution 4
Goal
|
Rank
|
Notes
|
Division of labor
|
Excellent
|
One group would write the mapping schema, based on detailed knowledge of the database. Another group would write the XSLT. No expertise required to write the transform code because this is handled by the XML Web control
|
One transaction, one place
|
Excellent
|
One trip to the database, and the dataset is available in one XML document
|
Look to the future
|
Good
|
Uses .NET XML Web control. Dataset is not tied to the data model as all element names are specified in the template (can be different from what's stored in the database). Complete control of the XML hierarchy is also available
|
Figure 22
Page Load
private void Page_Load(object sender, System.EventArgs e)
{
// Put user code to initialize the page here
dsPersons.ReadXml(new
XmlTextReader("http://localhost/pubs/templates/alldata.xml"));
dgperson.DataBind();
// Set the css class for all of the column headers
for (int i = 0; i < dgperson.Columns.Count; i++ )
dgperson.Columns[i].HeaderStyle.CssClass = "clsHeader";
}
Figure 24
Summary of Solution 5
Goal
|
Rank
|
Notes
|
Division of labor
|
Excellent
|
One group would write the mapping schema, based on detailed knowledge of the database. Another group required to configure the DataGrid
|
One transaction, one place
|
Excellent
|
One trip to the database, and the dataset is available in one XML document
|
Look to the future
|
Excellent
|
Uses .NET DataGrid Web control. The DataGrid has many possibilities for providing more enhanced features (paging, editing, validation, and so forth) that don't have to be hand-coded. Dataset is not tied to the data model as all element names are specified in the template (can be different from what's stored in the database). Complete control of the XML hierarchy is also available
|
Figure 25
Benchmarking Results
Solution Number
|
Name
|
HTTP-based Data Access
|
XSL Caching
|
Trips to DB
|
Execution Time (ms) **
|
How Much Faster than ASP/ADO Approach
|
1
|
ASP/ADO
|
|
|
49
|
1292
|
N/A
|
2
|
ManualXML/XSLT
|
|
|
49
|
324
|
4.0×
|
2A
|
ManualXML/XSLT
|
|

|
49
|
316
|
4.1×
|
3
|
DirectXML/XSLT
|
|
|
1
|
128
|
10.1×
|
3A
|
DirectXML/XSLT
|
|

|
1
|
140
|
9.2×
|
3B
|
DirectXML/XSLT
|

|
|
1
|
116
|
11.1×
|
3C
|
DirectXML/XSLT
|

|

|
1
|
129
|
10.0×
|
4
|
DirectXML/.NET (transform)
|

|
|
1
|
150
|
8.6×
|
5
|
DirectXML/.NET (DataGrid)
|

|
|
1
|
158
|
8.2×
|
* XSLT Transform, using cached stylesheet and XSLTemplate ** As recorded by Homer (time to last byte)
|
|