Inline XSD Schema Generation
In a FOR XML clause, you can request that your query return an inline schema together with the query results. If you want an XDR schema, you use the XMLDATA keyword in the FOR XML clause. If you want an XSD schema, you use the XMLSCHEMA keyword.
This topic describes the XMLSCHEMA keyword and explains the structure of the resulting inline XSD schema. Following are the limitations when you are requesting inline schemas:
- You can specify XMLSCHEMA only in RAW and AUTO mode, not in EXPLICIT mode.
- If a nested FOR XML query specifies the TYPE directive, the query result is of xml type, and this result is treated as an instance of untyped XML data. For more information, see TYPE Directive in FOR XML Queries and xml Data Type.
When you specify XMLSCHEMA in a FOR XML query, you receive both a schema and XML data, the query result. Each top-level element of the data refers to the previous schema by using a default namespace declaration that, in turn, refers to the target namespace of the inline schema.
For example:
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:schema="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="https://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
<xsd:element name="Production.ProductModel">
<xsd:complexType>
<xsd:attribute name="ProductModelID" type="sqltypes:int" use="required" />
<xsd:attribute name="Name" use="required">
<xsd:simpleType sqltypes:sqlTypeAlias="[AdventureWorks].[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:attribute>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<Production.ProductModel xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" ProductModelID="1" Name="Classic Vest" />
The result includes XML schema and the XML result. The <ProductModel
> top-level element in the result refers to the schema by using the default namespace declaration, xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" .
The schema part of the result may contain multiple schema documents that describe multiple namespaces. At a minimum, the following two schema documents are returned:
- One schema document for the Sqltypes namespace, and for which the base SQL types are being returned.
- Another schema document that describes the shape of the FOR XML query result.
Also, if any typed xml data types are included in the query result, the schemas associated with those typed xml data types are included.
The target namespace of the schema document that describes the shape of the FOR XML result contains a fixed portion and a numeric portion that increments automatically. The format of this namespace is shown in the following where n is a positive integer. For example, in the previous query, urn:schemas-microsoft-com:sql:SqlRowSet1 is the target namespace.
urn:schemas-microsoft-com:sql:SqlRowSetn
The change in the target namespaces in the result that occurred from one execution to another may not be desirable. For example, if you query the resulting XML, the change in target namespace will require that you update your query. You can optionally specify a target namespace when the XMLSCHEMA option is added in the FOR XML clause. The resulting XML will include the namespace you provided and will remain the same, regardless of how many times you run the query.
select ProductModelID, Name
from Production.ProductModel
where ProductModelID=1
FOR XML AUTO, XMLSCHEMA ('MyURI')
Entity Elements
In order to discuss the details of the XSD schema structure generated for the query result, the entity element has to first be described
An entity element in the XML data returned by FOR XML query is an element that is generated from a table and not from a column. For example, the following FOR XML query returns contact information from the Contact table in AdventureWorks database.
SELECT ContactID, FirstName
FROM Person.Contact
WHERE ContactID = 1
FOR XML AUTO, ELEMENTS
This is the result:
<Contact>
<ContactID>1</ContactID>
<FirstName>Syed</FirstName>
</Contact>
In this result, <Contact
> is the entity element. There can be multiple entity elements in the XML result and each of these has a global declaration in the inline XSD schema. For example, the following query retrieves sales order header and detail information for a specific order.
SELECT SalesOrderHeader.SalesOrderID, ProductID, OrderQty
FROM Sales.SalesOrderHeader, Sales.SalesOrderDetail
WHERE SalesOrderHeader.SalesOrderID = SalesOrderDetail.SalesOrderID
AND SalesOrderHeader.SalesOrderID=5001
FOR XML AUTO, ELEMENTS, XMLSCHEMA
Because the query specifies the ELEMENTS directive, the resulting XML is element-centric. The query also specifies the XMLSCHEMA directive. Therefore, an inline XSD schema is returned. This is the result:
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:schema="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="https://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
<xsd:element name="Sales.SalesOrderHeader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="SalesOrderID" type="sqltypes:int" />
<xsd:element ref="schema:Sales.SalesOrderDetail" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Sales.SalesOrderDetail">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ProductID" type="sqltypes:int" />
<xsd:element name="OrderQty" type="sqltypes:smallint" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Note the following from the previous query:
- In the result, <
SalesOrderHeader
> and <SalesOrderDetail
> are entity elements. Because of this, they are globally declared in the schema. That is, the declaration appears at the top level inside the <Schema
> element. - The <
SalesOrderID
>, <ProductID
>, and <OrderQty
> are not entity elements, because they map to columns. The column data is returned as elements in the XML, because of the ELEMENTS directive. These are mapped to local elements of the entity element's complex type. Note that if the ELEMENTS directive is not specified, the SalesOrderID, ProductID and OrderQty values are mapped to local attributes of the corresponding entity element's complex type.
Attribute Name Clashes
The following discussion is based on the CustOrder and CustOrderDetail tables. To test the following samples, create these tables and add your own sample data:
CREATE TABLE CustOrder (OrderID int primary key, CustomerID int)
go
CREATE TABLE CustOrderDetail (OrderID int, ProductID int, Qty int)
go
In FOR XML, the same name is sometimes used to indicate different properties, attributes. For example, the following attribute-centric RAW mode query generates two attributes that have the same name, OrderID. This generates an error.
SELECT CustOrder.OrderID,
CustOrderDetail.ProductID,
CustOrderDetail.OrderID
FROM dbo.CustOrder, dbo.CustOrderDetail
WHERE CustOrder.OrderID = CustOrderDetail.OrderID
FOR XML RAW, XMLSCHEMA
However, because it is acceptable to have two elements that have the same name, you can eliminate the problem by adding the ELEMENTS directive:
SELECT CustOrder.OrderID,
CustOrderDetail.ProductID,
CustOrderDetail.OrderID
from dbo.CustOrder, dbo.CustOrderDetail
where CustOrder.OrderID = CustOrderDetail.OrderID
FOR XML RAW, XMLSCHEMA, ELEMENTS
This is the result. Note in the inline XSD schema, the OrderID element is defined two times. One of the declarations has minOccurs set to 0, corresponding to the OrderID from the CustOrderDetail table, and the second one maps to the OrderID primary key column of the CustOrder table where minOccurs is 1 by default.
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" schemaLocation="https://schemas.microsoft.com/sqlserver/2004/sqltypes/sqltypes.xsd" />
<xsd:element name="row">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="OrderID" type="sqltypes:int" />
<xsd:element name="ProductID" type="sqltypes:int" minOccurs="0" />
<xsd:element name="OrderID" type="sqltypes:int" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Element Name Clashes
In FOR XML, the same name can be used to indicate two subelements. For example, the following query retrieves ListPrice and DealerPrice values of products, but the query specifies the same alias, Price, for these two columns. Therefore, the resulting rowset will have two columns with same name.
Case 1: Both subelements are nonkey columns of the same type and can be NULL
In the following query, both subelements are nonkey columns of the same type and can be NULL.
DROP TABLE T
go
CREATE TABLE T (ProductID int primary key, ListPrice money, DealerPrice money)
go
INSERT INTO T values (1, 1.25, null)
go
SELECT ProductID, ListPrice Price, DealerPrice Price
FROM T
for XML RAW, ELEMENTS, XMLSCHEMA
This is the corresponding XML generated. Only a fraction of the inline XSD is shown:
…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
<xsd:element name="row">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ProductID" type="sqltypes:int" />
<xsd:element name="Price" type="sqltypes:money" minOccurs="0" maxOccurs="2" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
<ProductID>1</ProductID>
<Price>1.2500</Price>
</row>
Note the following in the inline XSD schema:
- Both the ListPrice and DealerPrice are of the same type, money, and both can be NULL in the table. Therefore, because they may not be returned in the resulting XML, there is only one <
Price
> child element in the complex type declaration of the <row
> element that has minOccurs=0 and maxOccurs=2. - In the result, because the DealerPrice value is NULL in the table, only ListPrice is returned as a <
Price
> element. If you add the XSINIL parameter to the ELEMENTS directive, you will receive both of the elements that have the xsi:nil value set to TRUE for the<Price
> element that corresponds to DealerPrice. You will also receive two <Price
> child elements in the <row
> complex type definition in the inline XSD schema with the nillable attribute set to TRUE for both. This fragment is a partial result:
…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
<xsd:element name="row">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="ProductID" type="sqltypes:int" nillable="1" />
<xsd:element name="Price" type="sqltypes:money" nillable="1" />
<xsd:element name="Price" type="sqltypes:money" nillable="1" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ProductID>1</ProductID>
<Price>1.2500</Price>
<Price xsi:nil="true" />
</row>
Case 2: One key and one nonkey column of the same type
The following query illustrates one key and one nonkey column of the same type.
CREATE TABLE T (Col1 int primary key, Col2 int, Col3 nvarchar(20))
go
INSERT INTO T VALUES (1, 1, 'test')
go
The following query against table T specifies the same alias for Col1 and Col2, where Col1 is a primary key and cannot be null, and Col2 can be null. This generates two sibling elements that are children of the <row
> element.
SELECT Col1 as Col, Col2 as Col, Col3
FROM T
FOR XML RAW, ELEMENTS, XMLSCHEMA
This is the result. Only a fragment of the inline XSD schema is shown.
…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
<xsd:element name="row">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Col" type="sqltypes:int" />
<xsd:element name="Col" type="sqltypes:int" minOccurs="0" />
<xsd:element name="Col3" minOccurs="0">
<xsd:simpleType>
<xsd:restriction base="sqltypes:nvarchar"
sqltypes:localeId="1033"
sqltypes:sqlCompareOptions="IgnoreCase
IgnoreKanaType IgnoreWidth"
sqltypes:sqlSortId="52">
<xsd:maxLength value="20" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
<Col>1</Col>
<Col>1</Col>
<Col3>test</Col3>
</row>
Note in the inline XSD schema that the <Col
> element corresponding to the Col2 has minOccurs set to 0.
Case 3: Both elements of different types and corresponding columns can be NULL
The following query is specified against the sample table shown in case 2:
SELECT Col1, Col2 as Col, Col3 as Col
FROM T
FOR XML RAW, ELEMENTS, XMLSCHEMA
In the following query, Col2 and Col3 are given the same aliases. This generates two sibling elements that have the same name and that are both children of the <raw
> element in the result. Both of these columns are of different types and both can be NULL. This is the result. Only partial inline XSD schema is shown.
…
<xsd:schema targetNamespace="urn:schemas-microsoft-com:sql:SqlRowSet1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sqltypes="https://schemas.microsoft.com/sqlserver/2004/sqltypes" elementFormDefault="qualified">
<xsd:import namespace="https://schemas.microsoft.com/sqlserver/2004/sqltypes" />
<xsd:simpleType name="Col1">
<xsd:restriction base="sqltypes:int" />
</xsd:simpleType>
<xsd:simpleType name="Col2">
<xsd:restriction base="sqltypes:nvarchar" sqltypes:localeId="1033"
sqltypes:sqlCompareOptions="IgnoreCase
IgnoreKanaType IgnoreWidth" sqltypes:sqlSortId="52">
<xsd:maxLength value="20" />
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="row">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Col1" type="sqltypes:int" />
<xsd:element name="Col" minOccurs="0" maxOccurs="2" type="xsd:anySimpleType" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<row xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1">
<Col1>1</Col1>
<Col xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="Col1">1</Col>
<Col xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="Col2">test</Col>
</row>
Note the following in the inline XSD schema:
- Because both Col2 and Col3 can be NULL, the <
Col
> element declaration specifies the minOccurs as 0 and maxOccurs as 2. - Because both the <
Col
> elements are siblings, there is one element declaration in the schema. Also, because both of the elements are also of different types, though both are simple types, the type of the element in the schema is xsd:anySimpleType. In the result, each instance type is identified by the xsi:type attribute. - In the result, every instance of the <
Col
> element refers to its instance type by using the xsi:type attribute.