- •Iis и xml функции sql Server
- •Поддержка xml в Microsoft sql Server 2000
- •Примеры
- •Iis и xml функции sql Server
- •Шаблоны
- •Запросы xPath
- •Замечания по разделу
- •Возможности ado 2.5 Сохранение и загрузка из файла в формате xml
- •Выдача Recordset’а в формате xml непосредственно в поток Response
- •Трансформация с помощью domDocument
- •Возможности ado 2.6
- •Использование Command для формирования xml-документа на сервере
- •Выполнение шаблона
- •Выполнение запроса xPath
- •Шаблон с агрегатными функциями
- •Формирование xml-документа на клиенте с помощью ado
- •Апдейтаграммы и xml Bulk Load
- •Апдейтаграммы
- •Добавление данных
- •Обновление данных
- •Удаление
- •Создание новой таблицы и загрузка данных
- •Загрузка иерархических документов
- •Заключение
- •Литература
- •Предисловие
- •Настройка iis
- •Содержание wsdl-файла
- •Секция types
- •Секция message
- •Секция portType
- •Формы сообщений
- •Секция binding
- •Секция service
- •Создание Web-методов
- •Тестируем Web-сервис
- •Поддержка sqlxml в .Net Framework
- •Тестовый проект
- •Цели и ограничения
- •Реализация
- •Заключение
- •Литература
- •Тип данных xml Основы
- •Метод query
- •Метод value
- •Метод exist
- •Метод modify
- •Использование xml-типа
- •Хранение xml-типа
- •Поддержка xml Schema
- •Создание схемы
- •Удаление схемы
- •Ограничения текущей реализации
- •Безопасность
- •Вложенные запросы for xml
- •Генерация xsd на лету
- •Поддержка xQuery
- •Тело запроса
- •Конструкторы узлов
- •Операторы сравнения
- •Операторы сравнения атомарных значений
- •Общие операторы сравнения
- •Операторы сравнения узлов
- •Логические операторы
- •Условные выражения
- •Flwor-выражения
- •Кванторные выражения
- •Сортировка
- •Заключение
- •Литература
Хранение xml-типа
Мне было очень интересно узнать, каким образом SQL Server хранит XML-тип. К сожалению, в документации об этом нет ни слова, и вряд ли положение изменится к публичной бете. Вот что я выяснил из собственных экспериментов и общения с разработчиками.
XML-тип сохраняется как большой объект (lob - large object) в бинарном формате, структура которого неизвестна, но точно можно сказать, что она линейна. Т.е. сохраняется относительный порядок узлов в документе – атрибуты следуют после элемента, к которому они принадлежат, дочерние элементы следуют после атрибутов, и последующие элементы (элементы того же уровня) – после дочерних элементов.
Для осуществления запроса XQuery SQL Server каждый раз выполняет преобразование этой линейной структуры в реляционную – в таблицу узлов (node table). Таблица узлов более формализована и понятна, однако занимает в несколько раз (даже на порядки) больше места. Любой запрос XQuery трансформируется в SQL запрос к этой таблице.
Эта таблица может быть сохранена для последующих запросов, что и делается при построении XML-индекса.
Структура этой таблицы следующая (назначение некоторых полей я придумал сам, потому что разработчики пока не признаются, что эти поля означают):
pk1 – значение первичного ключа основной таблицы, где расположено XML-поле.
id – идентификатор строки в данной таблице.
nid – идентификатор узла.
tid – неизвестно.
value – значение узла.
lvalue – неизвестно.
lvaluebin – неизвестно.
hid – неизвестно.
xsinil – равно ли значение узла NULL.
xsitype – тип узла.
Вот собственно и все, что мне удалось раскопать по внутреннему устройству XML-типа.
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' go create 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 xml set @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 xml set @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 xml set @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 fams set 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 xml set @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
--Изменим текстовый узел элемента root set @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 xml set @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
--Изменим первый текстовый узел элемента root set @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 xml set @xml = '<?xml version="1.0" ?> <root> <a>1</a> <a>2</a> <a>30</a> </root>'
-- Добавляем инструкцию обработки как первый узел среди дочерних -- узлов элемента root set @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 с соответствующими значениями -- как последние узлы элемента root set @xml::modify(' insert (<sum>{sum(//a)}</sum>, element average {avg(//a)}) into /root ')
select @xml
-- Добавляем текстовый узел как первый дочерний узел элемента root set @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.
