Mapping an XML Schema to a Relational Schema Using Annotated Schemas

ADO and SQL Server

ADO and SQL Server

Mapping an XML Schema to a Relational Schema Using Annotated Schemas

An application can create XML views of relational data using annotated XDR (XML-Data Reduced) schemas, which can then be queried using XPath queries. This process is conceptually similar to creating views using Transact-SQL CREATE VIEW statements, and then specifying SQL queries against the view. Annotated schemas, which are Microsoft-developed extensions to the XML Data specification, allow client applications to view a relational database as an XML document instead of a group of tables. An XML file that maps XML elements and attributes to tables and columns of a relational database is called a Mapping Schema. Applications can use these two technologies to query Microsoft® SQL Server™ without using SQL commands and without knowing the relational design of the database. For more information about XPath queries, see Using XPath Queries. For more information about XML views and annotated schemas, see Creating XML Views Using Annotated XDR Schemas.

The following example demonstrates how to build an XPath query that is functionally equivalent to this Transact-SQL statement:

SELECT o.OrderID, o.OrderDate from Orders o, Customers c, 
WHERE o.CustomerID = c.CustomerID and c.CompanyName = ?

This example passes the CompanyName, Tortuga Restaurante, as an input parameter.

The Customers and Orders tables from the Northwind database are used to create a mapping schema. This is the structure of the Customers and Orders tables, including primary and foreign key relationships.

CREATE TABLE [Customers] 
   [CustomerID] [nchar] (5) NOT NULL ,
   [CompanyName] [nvarchar] (40) NOT NULL ,
   [ContactName] [nvarchar] (30) NULL ,
   [ContactTitle] [nvarchar] (30)  NULL ,
   [Address] [nvarchar] (60) NULL ,
   [City] [nvarchar] (15) NULL ,
   [Region] [nvarchar] (15) NULL ,
   [PostalCode] [nvarchar] (10) NULL ,
   [Country] [nvarchar] (15) NULL ,
   [Phone] [nvarchar] (24) NULL ,
   [Fax] [nvarchar] (24) NULL 

   PRIMARY KEY [CustomerID]

CREATE TABLE [Orders] (
   [OrderID] [int] IDENTITY (1, 1) NOT NULL,
   [CustomerID] [nchar] (5) NULL,
   [EmployeeID] [int] NULL,
   [OrderDate] [datetime] NULL,
   [RequiredDate] [datetime] NULL,
   [ShippedDate] [datetime] NULL,
   [ShipVia] [int] NULL,
   [Freight] [money] NULL,
   [ShipName] [nvarchar] (40) NULL,
   [ShipAddress] [nvarchar] (60) NULL,
   [ShipCity] [nvarchar] (15) NULL,
   [ShipRegion] [nvarchar] (15) NULL,
   [ShipPostalCode] [nvarchar] (10) NULL,
   [ShipCountry] [nvarchar] (15) NULL

   PRIMARY KEY [OrderID]
   FOREIGN KEY [Customers].[CustomerID]

The example SQL query requires the OrderID, OrderDate, and CustomerID columns from the Orders table, and the CompanyName and CustomerID columns from the Customers table.

The application also requires a mapping schema, which in this example, is stored in the Orders.xml file in the virtual root directory.

The document contains Namespace declarations, specifically the XML-Data namespace. These table-mapping elements are also included:

  • The sql:relation attribute, which is used to identify the table or view in the database. Inside each element are attributes that map to columns in the table identified by the element.

  • The sql:field attribute, which is used to identify the field in the SQL table.

  • The sql:relationship attribute, which is used to identify the primary and foreign key relationships between the two tables.
<?xml version="1.0" ?>
<Schema xmlns="urn:schemas-microsoft-com:xml-data"
           xmlns:dt="urn:schemas-microsoft-com:datatypes"
        xmlns:sql="urn:schemas-microsoft-com:xml-sql">

<ElementType name="Order" sql:relation="Orders" >
    <AttributeType name="CustomerID" />
    <AttributeType name="OrderID" />
    <AttributeType name="OrderDate" />
    <attribute type="CustomerID" sql:field="CustomerID" />
    <attribute type="OrderID" sql:field="OrderID" />
    <attribute type="OrderDate" sql:field="OrderDate" />
</ElementType>

<ElementType name="Customer" sql:relation="Customers" >
    <AttributeType name="CustomerID" />
    <AttributeType name="CompanyName" />
    <attribute type="CustomerID" sql:field="CustomerID" />
    <attribute type="CompanyName" sql:field="CompanyName" />
      <element type="Order" >
      <sql:relationship key-relation="Customers" key="CustomerID"
         foreign-key="CustomerID" foreign-relation="Orders" />
    </element>
</ElementType>
</Schema>

Using an Active Server Page (ASP), an application user generates a URL containing a company name for which he or she wants to see orders. In this example, the URL takes the form:

http://WebServer/Vroot/Orders.asp?CompanyName="Tortuga%20Restaurante"

Using the customer name passed in by the user, the ASP constructs this XPath query to run against the mapping schema:

Customer[@CompanyName="Tortuga Restaurante"]

This query string is passed to the ADO Command object and executed, returning the results in an XML stream.

The ASP begins by using the ASP Request object to capture the CompanyName passed in using the URL and storing it in a string variable called sCompanyName.

dim sCompanyName
sCompanyName = Request.QueryString("CompanyName") 

The application then creates ADO Connection and Command objects. Because the application issues commands written as XPATH queries, it must use the XPATH command dialect.

adoCmd.CommandText = "Customer[@CompanyName=" & sCompanyName & "]"
adoCmd.Dialect = "{ec2a4293-e898-11d2-b1b7-00c04f680c56}"

The application then sets properties specific to the Microsoft OLE DB Provider for SQL Server: Mapping Schema and Base Path. The application sets the Mapping Schema property to the name of the mapping schema file, and Base Path property to the directory containing the mapping schema file.

   adoCmd.Properties("Mapping Schema") = "Orders.xml"
   adoCmd.Properties("Base Path") = "C:\INETPUB\WWWROOT\Kowalski\"
   

After the Output Stream property is set to the ASP Response object, the command can be executed. The application sets the adExecuteStream parameter of the Command object, and encloses the setting in XML tags to create an XML data island.

Response.write "<XML ID='MyDataIsle'>"
   adoCmd.Execute , , adExecuteStream
   Response.write "</XML>"
%>

At this point in the code execution, the application has passed the XML stream to the client browser. The XML stream is displayed using client-side VBScript to bind the XML document to an instance of the DOM, and by looping through each child node to build a list of OrderIDs and OrderDates using HTML.

Examples

This is the complete code listing from the ASP described previously.

<HTML>
<HEAD>
<META NAME="GENERATOR" Content="Microsoft Developer Studio"/>
<META HTTP-EQUIV="Content-Type" content="text/html" charset="iso-8859-1"/>
<TITLE>XPATH Query Annotated Schema Orders.asp</TITLE>

<STYLE>
   BODY
   {
      FONT-FAMILY: Tahoma;
      FONT-SIZE: 8pt;
      OVERFLOW: auto
   }
   H3
   {
      FONT-FAMILY: Tahoma;
      FONT-SIZE: 8pt;
      OVERFLOW: auto
   }

</STYLE>

<!-- #include file="adovbs.inc" -->
<%
   dim sCompanyName
   sCompanyName = Request.QueryString("CompanyName")
   If Len(sCompanyName) = 0 then
      Response.redirect "http://MYSERVER1/Kowalski/OrdersErr.asp"
   Else
Dim sConn
sConn = "Provider=SQLOLEDB; Data Source=MYSERVER1; Initial Catalog=Northwind; 
User ID=SA;Password=;"

Dim adoConn
Set adoConn = Server.CreateObject("ADODB.Connection")
   adoConn.ConnectionString = sConn
adoConn.CursorLocation = adUseClient
adoConn.Open

      Dim adoCmd
          Set adoCmd = CreateObject("ADODB.Command")
          Set adoCmd.ActiveConnection = adoConn
          adoCmd.CommandText = "/Customer[@CompanyName=" & sCompanyName & "]"
adoCmd.Dialect = "{ec2a4293-e898-11d2-b1b7-00c04f680c56}"

       adoCmd.Properties("Mapping Schema") = "Orders.xml"
          adoCmd.Properties("Base Path") = "C:\INETPUB\WWWROOT\Kowalski\"
          adoCmd.Properties("Output Stream") = Response

Response.write "<XML ID='MyDataIsle'>"
      adoCmd.Execute , , adExecuteStream
      Response.write "</XML>"
End If
%>

<SCRIPT language="VBScript" For="window" Event="onload">
   
   Dim xmlDoc
   Set xmlDoc = MyDataIsle.XMLDocument
   xmlDoc.resolveExternals=false
   xmlDoc.async=false
      
   Dim root, child, header, OutputHeader
   Set root = xmlDoc.documentElement

   OutputHeader = document.all("header").innerHTML 
   OutputHeader = OutputHeader & "CustomerID: " & root.getAttribute("CustomerID")
   document.all("header").innerHTML = OutputHeader 
   
   For each child in root.childNodes
      dim OutputOrders, OrderList
      OutputOrders = document.all("Orders").innerHTML 
      OrderList = "<LI> Order # " & child.getAttribute("OrderID") & ", Date: " & 
child.getAttribute("OrderDate") & "</LI>"
      TotalPage = OutputOrders & OrderList
      document.all("Orders").innerHTML = TotalPage
   Next

</SCRIPT>

</HEAD>
<BODY>
   <H3>Client-side processing of XML Document MyDataIsle</H3>
   <DIV id=Header></DIV>
   <UL id=Orders></UL>
</BODY>
</HTML>

See Also

ADO and XPath Query