XML DML

На данный момент не существует стандарта и даже рабочей группы по вопросам создания языка для изменения содержимого XML-документов.

Существует самостоятельная инициатива разработчиков – XUpdate, которая представлена сообществом XMLDB (xmldb.org) и доступна по адресу [4]. Есть несколько продуктов, в том числе и коммерческих, поддерживающих этот язык, однако большого распространения он не получил.

Разработчики SQL Server 2005 решили пойти своим путем: они разработали язык xml dml – XML Data Manipulation Language, который является расширением XQuery. Он не выражается в терминах самого XML (как XUpdate), и в нем можно применять выражения XQuery. Расширение заключается во введении трех ключевых слов – update, delete и insert, которые можно использовать в методе modify XML-типа.

Ниже будем использовать несколько более сложный XML-документ, удовлетворяющий схеме my-first-schema:

drop xmlschema namespace 'my-first-schema'gocreate xmlschema '<xs:schema targetNamespace="my-first-schema" xmlns:tns="my-first-schema" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="root"> <xs:complexType> <xs:sequence> <xs:element name="a" maxOccurs="unbounded" minOccurs="0" form="qualified"/> </xs:sequence> </xs:complexType> </xs:element></xs:schema>'

Здесь мы расширили содержимое XML-элемента root: теперь он может содержать бесчисленное множество элементов a любого типа (так как тип не задан, используется по умолчанию xs:Any).

Delete

Это самый простой оператор. Вот его синтаксис

delete Expression

Где Expression – выражение XQuery, выбирающее последовательность узлов, подлежащих удалению. Оператор delete не может удалить узлы, находящиеся на оси namespace – это единственная ось, которая есть в XPath 2.0, но отсутствует в XQuery.

Любое выражение в XQuery возвращает последовательность элементов (sequence items). Элементами могут быть либо скалярные величины, либо узлы. Последовательность – совершенно новое понятие, которое заменяет понятие набора узлов в XPath 1.0, более подробно о нем мы поговорим в разделе "Поддержка XQuery".

Рассмотрим пример применения оператора delete:

declare @xml xmlset @xml = '<?xml version="1.0"?><root xmlns="test-unique"> <a>1</a> <a>2</a> <a>10</a></root>' set @xml::modify(' namespace tns="test-unique" delete /tns:root/tns:a[. = 10]') select @xml

Здесь удаляется узел a, значение которого равно 10. Единственное, что отличает это выражение от старого XPath – пролог, в котором декларируется пространство имен. Попробую продемонстрировать некоторые возможности XQuery:

declare @xml xmlset @xml = '<?xml version="1.0"?><root xmlns="test-unique"> <a>1</a> <a>2</a> <a>30</a></root>' set @xml::modify(' namespace tns="test-unique" delete if (sum(/tns:root/tns:a) > 10) then tns:root/tns:a[. = max(/tns:root/tns:a)] else /..') select @xml

В этом примере, если сумма всех числовых значений элементов a больше 10, удаляется XML-элемент а с максимальным значением. Иначе не удаляется ничего. Вместо конструкции "/.." по идее можно было бы использовать пустую последовательность "()". Но в имеющейся у меня бета-версии это приводило к ошибке. Поэтому пришлось применить эту конструкцию, обозначающую пустой элемент (точнее, несуществующего родителя узла документа).

Замечу, что при использовании функции max узлы а трактовались как строковые, так что если бы документ был следующего содержания:

<?xml version="1.0"?><root xmlns="test-unique"> <a>1</a> <a>2</a> <a>10</a></root>

то максимальным элементом оказался бы узел со значением 2. Чтобы выполнять операции над значениями элемента а как над числовыми, необходимо в схеме данных (my-first-schema) задать тип XML-элемента а. Есть другой способ, который заключается в непосредственном указании типа прямо в самом XML-документе с помощью атрибута type пространства имен http://www.w3.org/2001/XMLSchema-instance:

<?xml version="1.0"?><root xmlns="test-unique" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <a xsi:type="xs:integer">1</a> <a xsi:type="xs:integer">2</a> <a xsi:type="xs:integer">10</a></root>

Однако ни тот, ни другой способ текущей версией процессора в SQL Server не поддерживаются.

Следующий пример демонстрирует объединение результатов двух XPath-запросов в единую последовательность. В нем происходит удаление третьего элемента (в порядке их следования в документе) и элементов, чье числовое представление равно 1:

declare @xml xmlset @xml = '<?xml version="1.0"?><root xmlns="test-unique"> <a>1</a> <a>2</a> <a>1</a> <a>30</a></root>' set @xml::modify(' namespace tns="test-unique" delete (//tns:a[3], //tns:a[. = 1])') select @xml

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

Удалим из таблицы fams мужей из всех семей, чей суммарный доход превышает 440:

update famsset fam::modify('delete /fam/husband[sum(/fam/*/@income) > 440]')

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

Update

Этот оператор предназначен для изменения существующего значения узла. Синтаксис оператора:

update [value of] Expression1 to Expression2

Где:

  • value of – необязательное ключевое слово.
  • Expression1 – выражение XQuery, идентифицирующее один элемент, который должен являться узлом атрибута или текстовым узлом.
  • Expression2 – выражение XQuery, возвращающее последовательность из одного элемента, который может являться узлом любого типа, но не атомарным элементом. Если узел не является узлом атрибута или текстовым узлом, для получения значения этого узла применяется функция data.

Вот примеры использования данного оператора:

declare @xml xmlset @xml = '<?xml version="1.0"?><root>some text <a>1</a> <a>2</a> <a>30</a></root>' --Изменим значение элемента а, чье предыдущее значение --было равно минимальному, на сумму всех числовых--значений элементов аset @xml::modify(' update //a[. = min(//a)]/text() to sum(//a)') select @xml --Изменим текстовый узел элемента rootset @xml::modify(' update /root/text() to "another text"') select @xml

Вывод:

------------------------------------------------------<root>some text <a>33</a><a>2</a><a>30</a></root> (1 row(s) affected) ------------------------------------------------------<root>another text<a>33</a><a>2</a><a>30</a></root> (1 row(s) affected)

Как видите, везде при изменении значения XML-элемента приходится указывать дочерний текстовый узел. Однако даже этого недостаточно, когда XML-элемент содержит несколько текстовых узлов – в этом случае нужно точно указывать, какой именно текстовый узел необходимо изменить:

declare @xml xmlset @xml = '<?xml version="1.0"?><root>first text node <a b="9">1</a> <a>2</a> <a>30</a> second text node</root>' --Изменим значение атрибута b на среднее арифметическое--числовых значений элементов аset @xml::modify(' update //a/@b to avg(//a)') select @xml --Изменим первый текстовый узел элемента rootset @xml::modify(' update /root/text()[1] to "another text node"') select @xml

Вывод:

------------------------------------------------------<root>first text node <a b="11">1</a><a>2</a><a>30</a> second text node</root> (1 row(s) affected) ------------------------------------------------------<root>another text node<a b="11">1</a><a>2</a><a>30</a> second text node</root> (1 row(s) affected)

Insert

Последний оператор DML предназначен для вставки узлов в существующий XML-документ. Это самый мощный и сложный оператор. Рассмотрим его синтаксис:

insert Expression1 ({as first | as last} into | after | before Expression2)

Здесь:

  • Expression1 – выражение XQuery, определяющее вставляемую последовательность.
  • as first – ключевое слово, обозначающее вставку последовательности в первую позицию среди дочерних узлов данного XML-элемента.
  • as last – ключевое слово, обозначающее вставку последовательности в последнюю позицию узла. Если ничего не указано, используется as last.
  • into – ключевое слово, обозначающее вставку последовательности как дочерних узлов данного XML-элемента.
  • after – ключевое слово, обозначающее вставку последовательности как последующих узлов (following siblings) данного XML-элемента.
  • before - ключевое слово, обозначающее вставку последовательности как предыдущих узлов (preceding siblings) данного XML-элемента.
  • Expression2 – определяет узел, относительно которого производится вставка последовательности, указанной в Expression 1.

Ниже приводится пример работы данного оператора:

declare @xml xmlset @xml = '<?xml version="1.0"?><root> <a>1</a> <a>2</a> <a>30</a></root>' -- Добавляем инструкцию обработки как первый узел среди дочерних-- узлов элемента rootset @xml::modify(' insert <?mypi d="a"?> as first into /root') select @xml -- добавляем комментарий как дочерний узел элемента root-- после второго элемента аset @xml::modify(' insert <!--comment--> after /root/a[2]') select @xml -- Невозможно добавить узел пространства имен-- в элемент а. Не поддерживается в текущей версии-- set @xml::modify('-- insert namespace some {"rosa"} into /root/a[1]--'</str></str>) -- Добавляем атрибут attr со значением b во-- второй элемент аset @xml::modify(' insert attribute attr {"b"} into /root/a[2]') select @xml -- Добавляем два элемента: sum и avеrage с соответствующими значениями-- как последние узлы элемента rootset @xml::modify(' insert (<sum>{sum(//a)}</sum>, element average {avg(//a)}) into /root') select @xml -- Добавляем текстовый узел как первый дочерний узел элемента rootset @xml::modify(' insert <![CDATA[text node]]> as first into /root') select @xml

Вывод:

------------------------------------------------------<root><?mypi d="a"?><a>1</a><a>2</a><a>30</a></root> (1 row(s) affected) ------------------------------------------------------<root><?mypi d="a"?><a>1</a><a>2</a><!--comment--><a>30</a></root> (1 row(s) affected) ------------------------------------------------------<root><?mypi d="a"?><a>1</a><a attr="b">2</a><!--comment--><a>30</a></root> (1 row(s) affected) ------------------------------------------------------<root> <?mypi d="a"?> <a>1</a> <a attr="b">2</a> <!--comment--> <a>30</a> <sum>33</sum> <average>11</average></root> (1 row(s) affected) ------------------------------------------------------<root>text node<?mypi d="a"?> <a>1</a> <a attr="b">2</a> <!--comment--> <a>30</a> <sum>33</sum> <average>11</average></root> (1 row(s) affected)

В последних трех инструкциях данного примера я попытался применить конструкторы узлов – специальные выражения XQuery для создания узлов любого типа. К сожалению, конструкторы узлов пространств имен, документа, текста, комментария и инструкции обработки не поддерживаются в текущей версии. Однако часть из них – комментарии и инструкции обработки – можно задавать непосредственно как литералы, а для конструирования текстовых узлов можно использовать секцию CDATA, что и было продемонстрировано в примере. Более подробно о конструкторах узлов см. раздел Поддержка XQuery.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: