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


Применение режима EXPLICIT

Как описано в подразделе Конструирование XML используя FOR XML, режимы RAW и AUTO не предоставляют больших возможностей контроля формы XML, порождаемого из результата запроса. Однако режим EXPLICIT предоставляет наибольшую гибкость при формировании желаемого XML из результатов запроса.

Запрос в режиме EXPLICIT должен быть написан особым способом, при котором в запросе явно задаются дополнительные сведения о требуемом XML, такие как ожидаемый уровень вложенности в XML. В зависимости от запрашиваемого XML написание запросов режима EXPLICIT может оказаться весьма трудоемким. Может оказаться, что использование режима PATH с вложениями является более простой альтернативой написанию запросов в режиме EXPLICIT.

Поскольку требуемый XML описывается в режиме EXPLICIT в самом запросе, следует убедиться в том, что порождаемый XML имеет необходимую форму и является корректным.

Обработка наборов строк в режиме EXPLICIT

Режим EXPLICIT преобразует набор строк, получаемый в результате выполнения запроса, в XML-документ. Для того чтобы режим EXPLICIT создал XML-документ, набор строк должен иметь определенный формат. То есть необходимо написать запрос SELECT для создания набора строк, универсальной таблицы, имеющей определенный формат, так чтобы логика обработки могла создать желаемый XML.

Во-первых, запрос должен создавать следующие два столбца метаданных:

  • первый столбец должен предоставлять номер тега текущего элемента (целочисленного типа), с именем столбца Tag. В запросе должен быть указан уникальный номер тега для каждого элемента, который будет создан из набора строк;
  • второй столбец должен задавать номер тега для родительского элемента, и этот столбец должен иметь имя Parent. Таким образом, столбцы Tag и Parent предоставляют сведения об иерархии.

Значения этих столбцов метаданных вместе со сведениями в именах столбцов используются для создания желаемого XML. Обратите внимание на то, что имена столбцов в запросе должны задаваться особым образом. Также обратите внимание на то, что значение 0 или NULL в столбце Parent указывает на то, что у соответствующего элемента нет родителя. Такой элемент добавляется в XML в качестве элемента верхнего уровня.

Для понимания того, как универсальная таблица, сформированная запросом, преобразуется в результат XML, предположим, что написан запрос, который создает универсальную таблицу.

Образец универсальной таблицы

Обратите внимание на следующие особенности этой универсальной таблицы:

  • Первые два столбца Tag и Parent являются метастолбцами. Эти значения определяют иерархию.
  • Имена столбцов задаются особым образом, как описано выше в данном подразделе.
  • При формировании XML из этой универсальной таблицы данные таблицы секционируются вертикально на группы столбцов. Группировка определяется на основании значения Tag и имен столбцов. При конструировании XML логика обработки выбирает одну группу столбцов для каждой строки и создает элемент. В данном примере происходит следующее:
    • для значения столбца Tag, равного 1 в первой строке, столбцы, чьи имена включают тот же номер тэга, Customer!1!cid и Customer!1!name, формируют группу. Эти столбцы используются при обработке строки, и, как можно заметить, формируемый элемент приобретает вид <Customer id=... name=...>. Формат имени столбца описан ниже в этом подразделе;
    • для строк со значением столбца Tag, равным 2, столбцы Order!2!id и Order!2!date формируют группу, которая далее используется при конструировании элементов: <Order id=... date=... />;
    • для строк со значением столбца Tag, равным 3, столбцы OrderDetail!3!id!id и OrderDetail!3!pid!idref образуют группу. Каждая из этих строк формирует из данных столбцов элемент <OrderDetail id=... pid=...>;
  • обратите внимание на то, что при формировании иерархии XML строки обрабатываются по порядку. Иерархия XML определяется так, как показано далее.
    • Первая строка задает значение Tag, равное 1, и значение Parent, равное NULL. Поэтому соответствующий элемент <Customer> добавляется в качестве элемента верхнего уровня XML.

      <Customer cid="C1" name="Janine">
      
    • Вторая строка задает значение Tag, равное 2, и значение Parent, равное 1. Поэтому элемент <Order> добавляется в качестве потомка элемента <Customer>.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
      
    • Следующая строка задает значение Tag, равное 3, и значение Parent, равное 2. Поэтому два элемента <OrderDetail> добавляются в качестве потомков элемента <Order>.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
      
    • Последняя строка задает 2 в качестве номера Tag и 1 в качестве номера тэга Parent. Поэтому другой элемент-потомок <Order> добавляется к родительскому элементу <Customer>.

      <Customer cid="C1" name="Janine">
         <Order id="O1" date="1/20/1996">
            <OrderDetail id="OD1" pid="P1"/>
            <OrderDetail id="OD2" pid="P2"/>
         </Order>
         <Order id="O2" date="3/29/1997">
      </Customer>
      

В итоге, значения в метастолбцах Tag и Parent, сведения, указанные в именах столбцов, и правильный порядок строк создают желаемый XML при использовании режима EXPLICIT.

Упорядочение строк универсальной таблицы

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

Указание имен столбцов в универсальной таблице

При написании запросов в режиме EXPLICIT имена столбцов в результирующем наборе строк должны быть заданы с использованием описанного далее формата. Они предоставляют сведения о преобразовании, включая имена элементов и атрибутов и другие дополнительные сведения, задаваемые при помощи директив.

Общий формат имеет вид:

ElementName!TagNumber!AttributeName!Directive

Далее следует описание частей формата.

  • ElementName
    Результирующий общий идентификатор элемента. Например, если Customers задано в качестве ElementName, формируется элемент <Customers>.
  • TagNumber
    Уникальное значение тега, присвоенное элементу. Это значение с помощью двух метастолбцов, Tag и Parent, определяет вложенность элементов в результирующем XML.
  • AttributeName
    Предоставляет имя создаваемого атрибута для указанного элемента ElementName. Это поведение используется, если не задано значение Directive.

    Если задано значение Directive, имеющее тип xml, cdata или element, оно используется для создания дочернего элемента ElementName, к которому добавляется значение столбца.

    При указании значения Directive значение AttributeName может быть пустым. Например ElementName!TagNumber!!Directive. В этом случае значение столбца напрямую содержится в ElementName.

  • Directive
    Значение Directive является необязательным и может использоваться для предоставления дополнительных сведений для создания XML. Значение Directive служит двум целям.

    Одной из целей является кодирование значений в виде ID, IDREF и IDREFS. Можно указать ключевые слова ID, IDREF и IDREFS в качестве значений Directive. Эти директивы переопределяют типы атрибутов. Это позволяет создавать связи внутри документа.

    Также можно использовать значение Directive для указания того, как сопоставлять строковые данные с XML. Ключевые слова hide, element, elementxsinil, xml, xmltext и cdata также могут быть использованы в качестве значения Directive. Директива hide скрывает узел. Это полезно, если некоторые значения извлекаются только в целях сортировки и не должны появляться в результирующем XML.

    Директива element формирует содержащийся элемент вместо атрибута. Содержащиеся данные кодируются как сущность. Например символ < превращается в &lt;. Для значений столбцов, равных NULL, элемент не формируется. Если требуется, чтобы для столбцов со значениями NULL формировался элемент, можно указать директиву elementxsinil. При этом будет сформирован элемент с атрибутом xsi:nil=TRUE.

    Директива xml аналогична директиве element, однако при ее использовании не происходит кодирования сущности. Обратите внимание на то, что директива element может быть совмещена с ID, IDREF или IDREFS, в то время как директива xml не допускает совмещения с любой другой директивой, кроме hide.

    Директива cdata содержит данные путем упаковывания их с помощью раздела CDATA. Содержимое не кодируется в виде сущности. Первоначальный тип данных должен быть текстовым, например varchar, nvarchar, text или ntext. Эта директива может использоваться только совместно с hide. При использовании этой директивы не следует задавать значение AttributeName.

    Комбинирование директив из двух разных групп в большинстве случаев допустимо, однако комбинирование директив одной группы недопустимо.

    Если значения Directive и AttributeName не заданы, например Customer!1, подразумевается директива element, то есть Customer!1!!element, а данные столбца содержатся в ElementName.

    Если задана директива xmltext, содержимое столбца упаковывается в один тэг, который интегрируется с оставшейся частью документа. Эта директива полезна при выборке перегруженных, неиспользуемых XML-данных, сохраненных в столбец при помощи OPENXML. Дополнительные сведения см. в разделе Запросы XML с использованием OPENXML.

    Если задано значение AttributeName, имя тега заменяется указанным именем. В противном случае атрибут добавляется к текущему списку атрибутов включаемых элементов путем помещения содержимого в начало содержимого без кодирования сущности. Столбец с этой директивой должен быть текстового типа, например varchar, nvarchar, char, nchar, text или ntext. Эта директива может использоваться только cовместно с hide. Эта директива полезна при выборке перегруженных данных, хранящихся в столбце. Если содержимое не является корректным XML, поведение не определено.

Примеры

Следующие примеры иллюстрируют использование режима EXPLICIT.

A. Извлечение сведений о работнике

Этот пример извлекает идентификатор работника и имя работника для каждого работника. В базе данных AdventureWork значение employeeID может быть получено из таблицы Employee. Имена работников могут быть получены из таблицы Contact. Столбец ContactID может быть использован для соединения таблиц.

Предположим, что требуется произвести преобразование FOR XML EXPLICIT, чтобы сформировать XML так, как показано далее:

<Employee EmpID="1" >
  <Name FName="Guy" LName="Gilbert" />
</Employee>
...

Так как существует два уровня в иерархии, следует написать два запроса SELECT и применить предложение UNION ALL. Это первый запрос, который извлекает значения для элемента <Employee> и его атрибутов. Запрос присваивает полю Tag значение 1 для элемента <Employee>, а полю Parent значение NULL, так как это элемент верхнего уровня.

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

Это второй запрос. Он извлекает значения для элемента <Name>. Столбцу Tag присваивается значение 2 для элемента <Name>, а столбцу Parent — значение 1, в результате чего <Employee> используется в качестве родителя.

SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID

Совместив эти запросы с помощью UNION ALL, примените FOR XML EXPLICIT и укажите необходимое предложение ORDER BY. Сначала нужно отсортировать набор строк по значению EmployeeID, потом по имени, так что значения NULL в именах будут идти первыми. Выполняя следующий запрос без предложения FOR XML, можно увидеть сформированную универсальную таблицу.

Это окончательный запрос:

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName],
       NULL       as [Name!2!LName]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName]
FOR XML EXPLICIT

Промежуточный результат:

<Employee EmpID="1">
  <Name FName="Guy" LName="Gilbert" />
</Employee>
<Employee EmpID="2">
  <Name FName="Kevin" LName="Brown" />
</Employee>
...

Первая инструкция SELECT указывает имена для столбцов в результирующем наборе строк. Эти имена образуют две группы столбцов. Группа со значением Tag, равным 1, в имени столбца задает Employee в качестве элемента и EmpID в качестве атрибута. Другая группа столбцов со значением Tag, равным 2, в имени столбца задает <Name> в качестве элемента, а FName и LName в качестве атрибутов.

Следующая таблица показывает частичный набор строк, сформированный запросом:

Tag Parent  Employee!1!EmpID Name!2!FName Name!2!LName
----------- ----------- ---------------- -------------------
1    NULL     1                NULL          NULL
2     1       1                Guy           Gilbert
1    NULL     2                NULL          NULL
2     1       2                Kevin         Brown
1    NULL     3                NULL          NULL
2     1       3                Roberto       Tamburello 
...

Вот как строки универсальной таблицы обрабатываются для создания результирующего дерева XML:

Первая строка задает значение Tag, равное 1. В результате задается группа столбцов, у которых имеется значение Tag, равное 1, Employee!1!EmpID. Этот столбец задает Employee в качестве имени элемента. После этого создается элемент <Employee> с атрибутами EmpID. Соответствующие значения столбца присваиваются этим атрибутам.

Вторая строка имеет значение Tag, равное 2. В результате определена группа столбцов, в имени которых присутствует значение Tag, равное 2, Name!2!FName, Name!2!LName. Эти имена столбцов задают Name в качестве имени элемента. Элемент <Name> создается и имеет атрибуты FName и LName. После этого соответствующие значения столбца присваиваются этим атрибутам. Эта строка задает 1 в качестве Parent. Этот дочерний элемент добавляется к предыдущему элементу <Employee>.

Процесс повторяется для оставшихся строк набора строк. Обратите внимание на важность порядка строк в универсальной таблице, чтобы FOR XML EXPLICIT, обработав набор строк по порядку, мог создать желаемый XML.

Б. Указание директивы element

Этот пример похож на пример A, однако формирует XML с использованием элемента, как показано далее:

<Employee EmpID=...>
  <Name>
    <FName>...</FName>
    <LName>...</LName>
  </Name>
</Employee>

Запрос остается тем же, за исключением того, что добавлена директива ELEMENT в имена столбцов. Поэтому вместо атрибутов будут добавлены дочерние элементы <FName> и <LName> к элементу <Name>. Так как столбец Employee!1!EmpID не указывает директиву ELEMENT, EmpID добавляется в качестве атрибута элемента <Employee>.

SELECT 1 as Tag,
       NULL as Parent,
       EmployeeID as [Employee!1!EmpID],
       NULL       as [Name!2!FName!ELEMENT],
       NULL       as [Name!2!LName!ELEMENT]
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       FirstName, 
       LastName 
FROM   HumanResources.Employee E, Person.Contact C
WHERE  E.ContactID = C.ContactID
ORDER BY [Employee!1!EmpID],[Name!2!FName!ELEMENT]
FOR XML EXPLICIT

Частичный результат:

<Employee EmpID="1">
  <Name>
    <FName>Guy</FName>
    <LName>Gilbert</LName>
  </Name>
</Employee>
<Employee EmpID="2">
  <Name>
    <FName>Kevin</FName>
    <LName>Brown</LName>
  </Name>
</Employee>
...

В. Указание директивы elementxsinil

При указании директивы ELEMENT для извлечения XML с использованием элемента, если столбец имеет значение NULL, соответствующий элемент не будет сформирован при режиме EXPLICIT. Можно указать директиву ELEMENTXSINIL для того, чтобы запросить формирование элементов со значениями NULL, где атрибут xsi:nil установлен в значение TRUE.

Следующий запрос создает XML, включающий адрес работника. Для столбцов AddressLine2 и City в именах столбцов указана директива ELEMENTXSINIL. Это позволяет формировать элемент для значений NULL в столбцах AddressLine2 и City набора строк.

SELECT 1    as Tag,
       NULL as Parent,
       EmployeeID  as [Employee!1!EmpID],
       E.AddressID as [Employee!1!AddressID],
       NULL        as [Address!2!AddressID],
       NULL        as [Address!2!AddressLine1!ELEMENT],
       NULL        as [Address!2!AddressLine2!ELEMENTXSINIL],
       NULL        as [Address!2!City!ELEMENTXSINIL]
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.ContactID = A.ContactID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
       EmployeeID,
       E.AddressID,
       A.AddressID,
       AddressLine1, 
       AddressLine2,
       City 
FROM   HumanResources.EmployeeAddress E, Person.Address A
WHERE  E.AddressID = A.AddressID
ORDER BY [Employee!1!EmpID],[Address!2!AddressID]
FOR XML EXPLICIT

Промежуточный результат:

<Employee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        EmpID="1" AddressID="61">
  <Address AddressID="61">
    <AddressLine1>7726 Driftwood Drive</AddressLine1>
    <AddressLine2 xsi:nil="true" />
    <City>Monroe</City>
  </Address>
</Employee>
...

Г. Создание элементов с общим родителем при использовании режима EXPLICIT

Предположим, что требуется создать XML, который предоставляет сведения о заказах на продажу. Обратите внимание на то, что элементы <SalesPerson> и <OrderDetail> имеют одного и того же родителя. Каждый заказ имеет один элемент <OrderHeader>, один элемент <SalesPerson> или один или несколько элементов <OrderDetail>.

<OrderHeader SalesOrderID=... OrderDate=... CustomerID=... >
  <SalesPerson SalesPersonID=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=... />
  <OrderDetail SalesOrderID=... LineTotal=... ProductID=... OrderQty=.../>
      ...
</OrderHeader>
<OrderHeader ...</OrderHeader>

Следующий запрос в режиме EXPLICIT создает этот XML. Обратите внимание на то, что запрос указывает значение Tag, равное 1 для элемента <OrderHeader>, равное 2 для элемента <SalesPerson> и равное 3 для элемента <OrderDetail>. Так как элементы <SalesPerson> и <OrderDetail> имеют общего родителя, запрос указывает одно и то же значение Parent, равное 1, задавая элемент <OrderHeader>.

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE     SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT

Промежуточный результат:

<OrderHeader SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  <OrderDetail SalesOrderID="43659" LineTotal="28.840400" ProductID="716" OrderQty="1" />
  <OrderDetail SalesOrderID="43659" LineTotal="34.200000" ProductID="709" OrderQty="6" />
   ...
</OrderHeader>
<OrderHeader SalesOrderID="43661" OrderDate="2001-07-01T00:00:00" CustomerID="442">
  <SalesPerson SalesPersonID="282" />
  <OrderDetail SalesOrderID="43661" LineTotal="20.746000" ProductID="712" OrderQty="4" />
  <OrderDetail SalesOrderID="43661" LineTotal="40.373000" ProductID="711" OrderQty="2" />
   ...
</OrderHeader>

Д. Указание директив ID, IDREF

Этот пример аналогичен примеру В. Единственное отличие состоит в том, что запрос указывает директивы ID, IDREF. Эти директивы перезаписывают типы атрибута SalesPersonID в элементах <OrderHeader> и <OrderDetail>. Образуются связи внутри документа. Для просмотра перезаписанных типов необходима схема. Поэтому в запросе указывается параметр XMLDATA в предложении FOR XML для получения схемы.

SELECT  1 as Tag,
        0 as Parent,
        SalesOrderID  as [OrderHeader!1!SalesOrderID!id],
        OrderDate     as [OrderHeader!1!OrderDate],
        CustomerID    as [OrderHeader!1!CustomerID],
        NULL          as [SalesPerson!2!SalesPersonID],
        NULL          as [OrderDetail!3!SalesOrderID!idref],
        NULL          as [OrderDetail!3!LineTotal],
        NULL          as [OrderDetail!3!ProductID],
        NULL          as [OrderDetail!3!OrderQty]
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL 
SELECT 2 as Tag,
       1 as Parent,
        SalesOrderID, 
        NULL,
        NULL,
        SalesPersonID,  
        NULL,         
        NULL,         
        NULL,
        NULL         
FROM   Sales.SalesOrderHeader
WHERE  SalesOrderID=43659 or SalesOrderID=43661
UNION ALL
SELECT 3 as Tag,
       1 as Parent,
        SOD.SalesOrderID,
        NULL,
        NULL,
        SalesPersonID,
        SOH.SalesOrderID,
        LineTotal,
        ProductID,
        OrderQty   
FROM    Sales.SalesOrderHeader SOH,Sales.SalesOrderDetail SOD
WHERE   SOH.SalesOrderID = SOD.SalesOrderID
AND     (SOH.SalesOrderID=43659 or SOH.SalesOrderID=43661)
ORDER BY [OrderHeader!1!SalesOrderID!id], [SalesPerson!2!SalesPersonID],
         [OrderDetail!3!SalesOrderID!idref],[OrderDetail!3!LineTotal]
FOR XML EXPLICIT, XMLDATA

Частичный результат. Обратите внимание на то, что директивы ID, IDREF переписали в схеме типы данных атрибута SalesOrderID в элементах <OrderHeader> и <OrderDetail>. Если удалить эти директивы, схема вернет первоначальные типы этих атрибутов.

<Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="OrderHeader" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="id" />
    <AttributeType name="OrderDate" dt:type="dateTime" />
    <AttributeType name="CustomerID" dt:type="i4" />
    <attribute type="SalesOrderID" />
    <attribute type="OrderDate" />
    <attribute type="CustomerID" />
  </ElementType>
  <ElementType name="SalesPerson" content="mixed" model="open">
    <AttributeType name="SalesPersonID" dt:type="i4" />
    <attribute type="SalesPersonID" />
  </ElementType>
  <ElementType name="OrderDetail" content="mixed" model="open">
    <AttributeType name="SalesOrderID" dt:type="idref" />
    <AttributeType name="LineTotal" dt:type="number" />
    <AttributeType name="ProductID" dt:type="i4" />
    <AttributeType name="OrderQty" dt:type="i2" />
    <attribute type="SalesOrderID" />
    <attribute type="LineTotal" />
    <attribute type="ProductID" />
    <attribute type="OrderQty" />
  </ElementType>
</Schema>
<OrderHeader xmlns="x-schema:#Schema1" SalesOrderID="43659" OrderDate="2001-07-01T00:00:00" CustomerID="676">
  <SalesPerson SalesPersonID="279" />
  <OrderDetail SalesOrderID="43659" LineTotal="10.373000" ProductID="712" OrderQty="2" />
  ...
</OrderHeader>
...

Е. Указание директив ID, IDREFS

Атрибут элемента может быть указан в качестве типа атрибута ID, а атрибут IDREFS может быть использован для ссылки на него. Этим включаются связи внутри документа, которые похожи на связи первичного и внешнего ключей в реляционных базах данных.

Этот пример иллюстрирует, как директивы ID и IDREFS могут быть использованы для создания атрибутов с типами ID и IDREFS. Так как идентификаторы не могут быть целочисленными, значения ID в этом примере преобразуются. Другими словами, они подвергаются приведению типа. Префиксы используются для значений ID.

Предположим, что требуется создать следующий XML:

<Customer CustomerID="C1" SalesOrderIDList=" O11 O22 O33..." >
    <SalesOrder SalesOrderID="O11" OrderDate="..." />
    <SalesOrder SalesOrderID="O22" OrderDate="..." />
    <SalesOrder SalesOrderID="O33" OrderDate="..." />
    ...
</Customer>

Атрибут SalesOrderIDList элемента < Customer > является многозначным атрибутом, который ссылается на атрибут SalesOrderID элемента < SalesOrder >. Для установления этой связи атрибут SalesOrderID должен быть объявлен как имеющий тип ID, а атрибутSalesOrderIDList элемента < Customer> должен быть объявлен как имеющий тип IDREFS. Так как заказчик может оставить несколько заказов, используется тип IDREFS.

IDREFS также имеет более одного значения. Поэтому следует использовать отдельные предложения выборки, которые будут повторно использовать одни и те же сведения столбцов тега, родителя и ключевого столбца. После этого необходимо использовать предложение ORDER для того, чтобы убедиться, что последовательность строк, которая определяет значения IDREFS, появляется сгруппированной по родительскому элементу.

Далее запрос, который создает желаемый XML. В запросе используются директивы ID, IDREFS для перезаписи типов в именах столбцов (SalesOrder!2!SalesOrderID!ID, Customer!1!SalesOrderIDList!IDREFS).

SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID       [Customer!1!CustomerID],
        NULL               [Customer!1!SalesOrderIDList!IDREFS],
        NULL               [SalesOrder!2!SalesOrderID!ID],
        NULL               [SalesOrder!2!OrderDate]
FROM   Sales.Customer C 
UNION ALL 
SELECT  1 as Tag,
        0 as Parent,
        C.CustomerID,
        'O-'+CAST(SalesOrderID as varchar(10)), 
        NULL,
        NULL
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
UNION ALL
SELECT 2 as Tag,
       1 as Parent,
        C.CustomerID,
        NULL,
        'O-'+CAST(SalesOrderID as varchar(10)),
        OrderDate
FROM   Sales.Customer C, Sales.SalesOrderHeader SOH
WHERE  C.CustomerID = SOH.CustomerID
ORDER BY [Customer!1!CustomerID] ,
         [SalesOrder!2!SalesOrderID!ID],
         [Customer!1!SalesOrderIDList!IDREFS]
FOR XML EXPLICIT

Ж. Использование директивы hide для сокрытия элементов и атрибутов результирующего XML

Этот пример иллюстрирует использование директивы hide. Эта директива полезна, если требуется, чтобы запрос возвращал атрибут сортировки строк в универсальной таблице, возвращаемой запросом, но этот атрибут не должен появиться в конечном XML-документе.

Этот запрос создает соответствующий XML:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
           <Summary> element from XML stored in CatalogDescription column
    </SummaryDescription>
  </Summary>
</ProductModel>

Этот запрос формирует желаемый XML. В запросе задаются две группы столбцов со значениями Tag, равными 1 и 2, в именах столбцов.

В этом запросе используется метод query() (тип данных xml) типа данных xml для выполнения запроса к столбцу CatalogDescription типа xml с целью получить описание итога. В запросе также используется метод value() (тип данных xml) типа данных xml для получения значения ProductModelID из столбца CatalogDescription. Это значение не должно присутствовать в конечном XML, но оно необходимо для сортировки конечного набора строк. Поэтому имя столбца, [Summary!2!ProductModelID!hide], включает директиву hide. Если этот столбец не включен в инструкцию SELECT, то придется отсортировать набор строк по [ProductModel!1!ProdModelID] и[Summary!2!SummaryDescription], которые имеют тип xml, а использовать столбец типа xml в предложении ORDER BY нельзя. Поэтому добавляется дополнительный столбец [Summary!2!ProductModelID!hide], и в предложении ORDER BY указывается именно он.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID     as [ProductModel!1!ProdModelID],
        Name               as [ProductModel!1!Name],
        NULL               as [Summary!2!ProductModelID!hide],
        NULL               as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
        CatalogDescription.value('
         declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
       (/PD:ProductDescription/@ProductModelID)[1]', 'int'),
        CatalogDescription.query('
         declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
         /pd:ProductDescription/pd:Summary')
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],[Summary!2!ProductModelID!hide]
FOR XML EXPLICIT
go

Результат:

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <pd:Summary xmlns:pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription" >
        <p1:p xmlns:p1="http://www.w3.org/1999/xhtml">Our top-of-the-line competition mountain bike. Performance-enhancing options include the innovative HL Frame, super-smooth front suspension, and traction for all terrain. </p1:p>
      </pd:Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

З. Указание директивы element и кодирование сущности

Этот пример иллюстрирует различие между директивами element и xml. Директива element преобразует данные в сущность, а директива xml не делает этого. Элемент <Summary> является назначенным XML, <Summary>This is summary description</Summary>, в запросе.

Рассмотрим этот запрос:

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription!ELEMENT]
FROM    Production.ProductModel
WHERE   ProductModelID=19
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        NULL,
       '<Summary>This is summary description</Summary>'
FROM   Production.ProductModel
WHERE  ProductModelID=19
FOR XML EXPLICIT

Результат. Описание итога преобразуется в сущность в результате.

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>&lt;Summary&gt;This is summary description&lt;/Summary&gt;</SummaryDescription>
  </Summary>
</ProductModel>

Теперь, если указать директиву xml в имени столбца, Summary!2!SummaryDescription!xml, вместо директивы element, в результате будет получено описание итога без преобразования в сущность.

<ProductModel ProdModelID="19" Name="Mountain-100">
  <Summary>
    <SummaryDescription>
      <Summary>This is summary description</Summary>
    </SummaryDescription>
  </Summary>
</ProductModel>

Вместо присвоения статического значения XML в следующем запросе используется метод query() типа xml для получения описания итога модели продукта из столбца CatalogDescription типа xml. Так как известно, что результат будет иметь тип xml, преобразование в сущность не применяется.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        NULL            as [Summary!2!SummaryDescription]
FROM    Production.ProductModel
WHERE   CatalogDescription is not null
UNION ALL
SELECT  2 as Tag,
        1 as Parent,
        ProductModelID,
        Name,
       (SELECT CatalogDescription.query('
            declare namespace pd="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
          /pd:ProductDescription/pd:Summary'))
FROM     Production.ProductModel
WHERE    CatalogDescription is not null
ORDER BY [ProductModel!1!ProdModelID],Tag
FOR XML EXPLICIT

И. Указание директивы cdata

Если указана директива cdata, содержащиеся данные не будут закодированы в сущность, а будут помещены в раздел CDATA. Атрибуты cdata должны быть безымянными.

Следующий запрос упаковывает описания итога модели продукта в раздел CDATA.

SELECT  1 as Tag,
        0 as Parent,
        ProductModelID  as [ProductModel!1!ProdModelID],
        Name            as [ProductModel!1!Name],
        '<Summary>This is summary description</Summary>'   
            as [ProductModel!1!!cdata] -- no attribute name so ELEMENT assumed
FROM    Production.ProductModel
WHERE   ProductModelID=19
FOR XML EXPLICIT

Результат:

<ProductModel ProdModelID="19" Name="Mountain-100">
   <![CDATA[<Summary>This is summary description</Summary>]]>
</ProductModel>

К. Указание директивы xmltext

Этот пример иллюстрирует, как данные в переполненном столбце направляются на обработку при помощи директивы xmltext в инструкции SELECT, использующей режим EXPLICIT.

Рассмотрим таблицу Person. Эта таблица имеет столбец Overflow, в котором хранится неиспользуемая часть XML-документа.

CREATE TABLE Person(PersonID varchar(5), PersonName varchar(20), Overflow nvarchar(200))
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P">content</SomeTag>')

Этот запрос извлекает столбцы из таблицы Person. Для столбца Overflow значение AttributeName не задано, но значение Directive установлено в xmltext как часть имени столбца универсальной таблицы.

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- No AttributeName; xmltext directive
FROM Person
FOR XML EXPLICIT

В результирующем XML-документе:

  • так как значение AttributeName не указано для столбца Overflow, а директива xmltext указана, атрибуты элемента <overflow> добавляются в список атрибутов содержащего его элемента <Parent>;
  • так как атрибут PersonID элемента <xmltext> конфликтует с атрибутом PersonID, извлеченным на том же уровне элемента, атрибут элемента <xmltext> игнорируется, даже если PersonID равен NULL. Обычно атрибут замещает атрибут с тем же именем при переполнении.

Результат:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">content</Parent>

Если переполненные данные имеют вложенные элементы и указан тот же запрос, вложенные элементы в столбце Overflow добавляются как вложенные элементы содержащего его элемента <Parent>.

Например, можно изменить данные в таблице Person так, чтобы столбец Overflow имел вложенные элементы.

TRUNCATE TABLE Person
INSERT INTO Person VALUES ('P1','Joe',N'<SomeTag attr1="data">content</SomeTag>')
INSERT INTO Person VALUES ('P2','Joe',N'<SomeTag attr2="data"/>')
INSERT INTO Person VALUES ('P3','Joe',N'<SomeTag attr3="data" PersonID="P"><name>PersonName</name></SomeTag>')

Если выполнен тот же запрос, вложенные элементы элемента <xmltext> добавляются как вложенные элементы содержащего его элемента <Parent>:

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!!xmltext] -- no AttributeName, xmltext directive
FROM Person
FOR XML EXPLICIT

Результат:

<Parent PersonID="P1" PersonName="Joe" attr1="data">content</Parent>
<Parent PersonID="P2" PersonName="Joe" attr2="data"></Parent>
<Parent PersonID="P3" PersonName="Joe" attr3="data">
  <name>PersonName</name>
</Parent>

Если указано значение AttributeName вместе с директивой xmltext, атрибуты элемента <overflow> добавляются как атрибуты вложенных элементов содержащего его элемента <Parent>. Имя, указанное как AttributeName, становится именем вложенного элемента

В этом запросе значение AttributeName, <overflow>, указано вместе с директивой xmltext*:*

SELECT 1 as Tag, NULL as parent,
       PersonID as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName],
       overflow as [Parent!1!overflow!xmltext] -- overflow is AttributeName
                      -- xmltext is directive
FROM Person
FOR XML EXPLICIT

Результат:

<Parent PersonID="P1" PersonName="Joe">
  <overflow attr1="data">content</overflow>
</Parent>
<Parent PersonID="P2" PersonName="Joe">
  <overflow attr2="data" />
</Parent>
<Parent PersonID="P3" PersonName="Joe">
  <overflow attr3="data" PersonID="P">
    <name>PersonName</name>
  </overflow>
</Parent>

В этом элементе запроса значение directive задано для атрибута PersonName. Это приводит к тому, что в PersonName добавляется в качестве вложенного элемента содержащего его элемента <Parent>. Атрибуты <xmltext> все так же добавляются к окружению элемента <Parent>. Содержимое элемента <overflow> и вложенные элементы, добавляются в начало других вложенных элементов содержащих их элементов <Parent>.

SELECT 1      as Tag, NULL as parent,
       PersonID   as [Parent!1!PersonID],
       PersonName as [Parent!1!PersonName!element], -- element directive
       overflow   as [Parent!1!!xmltext]
FROM Person
FOR XML EXPLICIT

Результат:

<Parent PersonID="P1" attr1="data">content<PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P2" attr2="data">
  <PersonName>Joe</PersonName>
</Parent>
<Parent PersonID="P3" attr3="data">
  <name>PersonName</name>
  <PersonName>Joe</PersonName>
</Parent>

Если столбец данных xmltext содержит атрибуты корневого элемента, эти атрибуты не показываются в XML-схеме данных, а синтаксический анализатор MSXML не проверяет результирующий фрагмент XML-документа. Например:

SELECT 1 as Tag,
       0 as Parent,
       N'<overflow a="1"/>' as 'overflow!1!!xmltext'
FOR XML EXPLICIT, xmldata

Результат. Обратите внимание на то, что в возвращаемой схеме атрибут переполнения a отсутствует:

<Schema name="Schema2" 
        xmlns="urn:schemas-microsoft-com:xml-data" 
        xmlns:dt="urn:schemas-microsoft-com:datatypes">
  <ElementType name="overflow" content="mixed" model="open">
  </ElementType>
</Schema>
<overflow xmlns="x-schema:#Schema2" a="1">
</overflow> 

См. также

Справочник

Использование режима RAW
Использование режима AUTO
Создание XML с помощью предложения FOR XML

Основные понятия

Использование режима PATH

Другие ресурсы

SELECT (Transact-SQL)

Справка и поддержка

Получение помощи по SQL Server 2005