Поделиться через


Создание встроенных XSD-схем

В предложении FOR XML можно запросить, чтобы запрос возвращал встроенную схему вместе с результатами запроса. Если нужно получить XDR-схему, то в предложении FOR XML следует использовать ключевое слово XMLDATA. Если нужно получить XSD-схему, то тогда следует использовать ключевое слово XMLSCHEMA.

В этом разделе описывается ключевое слово XMLSCHEMA и объясняется структура результирующей встроенной XSD-схемы. Далее приведены ограничения, возникающие при запросе встроенных схем.

  • Параметр XMLSCHEMA можно задать только в режимах RAW и AUTO, этого нельзя сделать в режиме EXPLICIT.

  • Если во вложенном запросе FOR XML определена директива TYPE, то выводится результат запроса типа данных xml, и этот результат принимается за экземпляр нетипизированных XML-данных. Дополнительные сведения см. в разделе Реализация языка XML в SQL Server.

Если в запросе FOR XML задан параметр XMLSCHEMA, то в результат запроса входит и схема, и XML-данные. Каждый элемент данных высшего уровня ссылается на предыдущую схему посредством заданного по умолчанию объявления пространства имен, которое, в свою очередь, ссылается на целевое пространство имен встроенной схемы.

Например:

<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" />

В результат входит XML-схема и XML-результат. Элемент результата высшего уровня <ProductModel> ссылается на схему посредством заданного по умолчанию объявления пространства имен, xmlns="urn:schemas-microsoft-com:sql:SqlRowSet1".

В присутствующей в результате схеме может находиться множество документов схемы, описывающих различные пространства имен. Возвращаться должны, как минимум, следующие две схемы.

  • Один документ схемы для пространства имен Sqltypes, для которого возвращаются базовые типы SQL.

  • Другой документ схемы, описывающий форму результата запроса FOR XML.

А также, если в результат запроса входят какие-либо типизированные типы данных xml, то возвращаются схемы, связанные с типами данных xml.

Целевое пространство имен документа схемы, описывающее форму результата FOR XML, содержит фиксированную часть и числовую часть, которая автоматически увеличивается. Далее показан формат данного пространства имен, где n является положительным целым числом. Например, в предыдущем запросе целевым пространством имен было urn:schemas-microsoft-com:sql:SqlRowSet1.

urn:schemas-microsoft-com:sql:SqlRowSetn

Изменения в целевых пространствах имен в результате, произошедшем между двумя выполнениями, могут быть нежелательны. Например, если запрашивается результирующий XML, то при изменении в целевых пространствах имен необходимо обновить запрос. При необходимости можно задать целевое пространство имен, когда в предложение FOR XML добавлен параметр XMLSCHEMA. Результирующий XML будет содержать указанное пространство имен и оставаться неизменным независимо от того, сколько раз выполняли запрос.

select ProductModelID, Name
from   Production.ProductModel
where ProductModelID=1
FOR XML AUTO, XMLSCHEMA ('MyURI')

Элементы сущности

Для описания особенностей создания структуры XSD-схемы для результата запроса необходимо вначале привести общие сведения про элемент сущности.

Элемент сущности XML-данных, возвращаемых запросом FOR XML, является элементом, создаваемым таблицей, а не столбцом. Например, следующий запрос FOR XML возвращает контактную информацию из таблицы Контакты базы данных AdventureWorks.

SELECT ContactID, FirstName
FROM Person.Contact
WHERE ContactID = 1
FOR XML AUTO, ELEMENTS

Результат:

  <Contact>
    <ContactID>1</ContactID>
    <FirstName>Syed</FirstName>
  </Contact>

В этом результате элементом сущности является <Contact>. В XML-результате может быть множество элементов сущности, и каждый из них должен обладать глобальным объявлением во встроенной XSD-схеме. Например, следующий запрос извлекает из заголовка сведения о заказах на продажу, а также получает подробные данные о конкретном заказе.

SELECT  SalesOrderHeader.SalesOrderID, ProductID, OrderQty
FROM    Sales.SalesOrderHeader, Sales.SalesOrderDetail
WHERE   SalesOrderHeader.SalesOrderID = SalesOrderDetail.SalesOrderID
AND     SalesOrderHeader.SalesOrderID=5001
FOR XML AUTO, ELEMENTS, XMLSCHEMA

Из-за того, что в запрос добавлена директива ELEMENTS, XML-результат формируется с использованием элементов. Также в этом запросе задана директива XMLSCHEMA, поэтому возвращается встроенная XSD-схема. Результат:

<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>

Обратите внимание на следующие данные из предыдущего запроса.

  • В этом результате элементами сущности являются <SalesOrderHeader> и <SalesOrderDetail>. Из-за этого они глобально объявлены в схеме. То есть объявление появляется на высшем уровне элемента <Schema>.

  • <SalesOrderID>, <ProductID> и <OrderQty> не являются элементами сущности, так как они сопоставлены столбцам. Из-за директивы ELEMENTS данные столбца возвращаются в виде элементов в XML. Они сопоставлены локальным элементам сложного типа элемента сущности. Заметьте, что если директива ELEMENTS не задана, то значения SalesOrderID, ProductID и OrderQty сопоставляются с локальными атрибутами соответствующего сложного типа элемента сущности.

Конфликты имен атрибутов

Следующее обсуждение построено на таблицах CustOrder и CustOrderDetail. Для проверки следующих образцов создайте такие же таблицы и добавьте собственные образцы данных:

CREATE TABLE CustOrder (OrderID int primary key, CustomerID int)
go
CREATE TABLE CustOrderDetail (OrderID int, ProductID int, Qty int)
go

В предложении FOR XML одинаковые имена иногда соответствуют различным свойствам и атрибутам. Например, следующий атрибутивный запрос режима RAW создает два атрибута с одним и тем же именем — OrderID. Это приводит к ошибке.

SELECT CustOrder.OrderID, 
       CustOrderDetail.ProductID, 
       CustOrderDetail.OrderID
FROM   dbo.CustOrder, dbo.CustOrderDetail
WHERE  CustOrder.OrderID = CustOrderDetail.OrderID
FOR XML RAW, XMLSCHEMA

Однако так как двум элементам допустимо иметь одно и то же имя, можно решить проблему добавлением директивы ELEMENTS:

SELECT CustOrder.OrderID,
       CustOrderDetail.ProductID, 
       CustOrderDetail.OrderID
from   dbo.CustOrder, dbo.CustOrderDetail
where  CustOrder.OrderID = CustOrderDetail.OrderID
FOR XML RAW, XMLSCHEMA, ELEMENTS

Результат. Обратите внимание, что во встроенной XSD-схеме элемент OrderID определяется два раза. Параметр minOccurs одного из объявлений, соответствующего OrderID из таблицы CustOrderDetail, равен 0, а параметр второго объявления, соответствующего столбцу первичного ключа OrderID таблицы CustOrder, равен значению 1 по умолчанию.

<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>

Конфликты имен элементов

В предложении FOR XML одинаковые имена иногда соответствуют различным подэлементам. Например, следующий запрос получает значения продуктов ListPrice и DealerPrice, но определяет для этих двух столбцов один и тот же псевдоним Price. Поэтому в результирующем наборе строк будут присутствовать два столбца с одинаковым именем.

Случай 1. Оба подэлемента являются неключевыми столбцами одинакового типа и могут иметь значение NULL

В следующем запросе оба подэлемента являются неключевыми столбцами одинакового типа и могут иметь значение 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

Это получаемый в результате XML. Показана только часть встроенной XSD:

…
<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>

Обратите внимание на следующее во встроенной XSD-схеме:

  • Оба значения, и ListPrice, и DealerPrice, имеют один и тот же тип, money, и оба могут иметь значение NULL в таблице. Таким образом, из-за того, что они могут не возвращаться в результирующем XML, в объявлении сложного типа элемента <row> может быть только один дочерний элемент <Price> с параметрами minOccurs=0 и maxOccurs=2.

  • В результате из-за того, что значение DealerPrice в таблице равно нулю, в качестве элемента <Price> возвращается только значение ListPrice. При добавлении параметра XSINIL к директиве ELEMENTS будут получены оба элемента со значением xsi:nil, равными TRUE для элемента <Price>, соответствующего DealerPrice. Также будут получены два дочерних элемента <Price> в определении сложного типа <row> встроенной XSD-схемы с атрибутом nillable, равным TRUE для обоих. Это промежуточный результат:

…
<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>

Случай 2. Один ключевой столбец и один неключевой столбец одинакового типа

В следующем запросе находится один ключевой столбец и один неключевой столбец одинакового типа.

CREATE TABLE T (Col1 int primary key, Col2 int, Col3 nvarchar(20))
go
INSERT INTO T VALUES (1, 1, 'test')
go 

Следующий запрос по отношению к таблице T задает один и тот же псевдоним для Col1 и Col2, где Col1 является столбцом первичного ключа и не может быть нулевым, а Col2 может быть нулевым. Это приводит к созданию двух элементов, являющихся дочерними элементами элемента <row>.

SELECT Col1 as Col, Col2 as Col, Col3
FROM T
FOR XML RAW, ELEMENTS, XMLSCHEMA

Результат. Показана только часть встроенной XSD-схемы:

…
<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>

Обратите внимание, что во встроенной XSD-схеме элемент <Col> соответствует столбцу Col2 с параметром minOccurs, равным 0.

Случай 3. Оба элемента различных типов и соответствующие столбцы могут иметь значение NULL

Для случая 2 показан следующий запрос, заданный относительно таблицы-образца:

SELECT Col1, Col2 as Col, Col3 as Col
FROM T
FOR XML RAW, ELEMENTS, XMLSCHEMA

В этом запросе Col2 и Col3 обладают одинаковыми псевдонимами. Это приводит к появлению двух элементов с одинаковыми именами, являющихся дочерними элементами элемента <raw> в результате. Оба столбца принадлежат различным типам и могут иметь значение NULL. Результат. Показана только часть встроенной XSD-схемы.

…
<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>

Обратите внимание на следующее во встроенной XSD-схеме.

  • Так как Col2 и Col3 могут иметь значение NULL, то объявление элемента Col> определяет minOccurs равным 0, а maxOccurs равным 2.

  • Из-за того, что элементы <Col> имеют общего родителя, в схеме присутствует только одно объявление элемента. Также из-за того, что элементы обладают различными, хотя и простыми типами, типом элемента в схеме является xsd:anySimpleType. В результате тип каждого экземпляра определяется атрибутом xsi:type.

  • В результате каждый экземпляр элемента <Col> ссылается на свой тип экземпляра при помощи атрибута xsi:type.