- •Iis и xml функции sql Server
- •Поддержка xml в Microsoft sql Server 2000
- •Примеры
- •Iis и xml функции sql Server
- •Шаблоны
- •Запросы xPath
- •Замечания по разделу
- •Возможности ado 2.5 Сохранение и загрузка из файла в формате xml
- •Трансформация с помощью domDocument
- •Выдача Recordset’а в формате xml непосредственно в поток Response
- •Возможности 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-выражения
- •Кванторные выражения
- •Сортировка
- •Заключение
- •Литература
Логические операторы
Логические операторы and и or остались на своем месте. Не поменялась ни семантика, ни синтаксис. В качестве типов обоих операндов всегда выступает логический тип – xs:boolean. Если операнд имеет отличный от xs:boolean тип, для него находится действительное булево значение (effective boolean value) путем применения функции fn:boolean.
Функция fn:boolean возвращает false в случае, когда операнд:
Пустая последовательность.
Логическое значение false.
Строка нулевой длины.
Числовое значение, равное нулю.
Вещественное значение, равное NaN – not a number.
Во всех остальных случаях функция возвращает true.
Например:
declare @xml xml set @xml = '<a>1</a><a>2</a>' select @xml::query(' {-- true - действительное булево значение 2 является true --} true() and 2 , {-- false - второй операнд является пустой последовательностью --} true() and () , {-- true - действительное булево значение обоих операндов - true --} "rosa" and "dima" , {-- выберет оба узла а --} /a[. = 1 or . = 2] ') |
К сожалению, SQL Server не захотел принимать этот пример из-за последнего выражения, где выбираются XML-элементы a, выдавая такую ошибку: XQuery: Heterogeneous sequences are not allowed: found 'xs:boolean +' and 'element a *'. Очередной баг.
Условные выражения
Условное выражение присутствует в XQuery почти в паскалевском синтаксисе – if then else. Порядок обработки этой конструкции следующий: для выражения после if вычисляется эффективное булево значение. Если результат равен истине, вычисляется выражение после then, если ложь, вычисляется выражение после else. Конструкция else обязательна. Выражение после if обязано заключаться в скобки.
Например:
declare @xml xml set @xml = '' select @xml::query('if (1 eq 2) then "alex" else "rosa"') |
В качестве выражений then и else в этом примере используются конструкторы текстовых узлов.
Flwor-выражения
FLWOR расшифровывается как for let where order by return. К сожалению, SQL Server не поддерживает ключевых слов LET и ORDER BY.
Бессмысленно говорить об отличиях в реализации SQL Server от текущего черновика стандарта, потому что фактически у них нет ничего общего, кроме трех ключевых слов – for, where и return.
FLWOR-выражения, являются, пожалуй самым мощным средством обработки последовательностей в XQuery. Синтаксис выражения в SQL Server примерно следующий (примерно, потому что в документации по SQL Server нигде не указан точный синтаксис FLWOR-выражения):
("for" "$" VariableName "in" Expression) + ("where" WhereExpression)? "return" ReturnExpression |
Где:
VariableName – имя переменной.
Expression – выражение XQuery, возвращающее последовательность.
Смысл выражения FLWOR: в цикле каждый элемент последовательности, возвращаемой Expression, связывается с переменной, чье имя задается в VariableName. Далее, в каждой итерации выполняется выражение WhereExpression, и если его результат равен true – выполняется выражение ReturnExpression. Важно понимать, что ReturnExpression выполняется для каждого элемента последовательности, т.е. n раз, где n – размер последовательности. Если выражение WhereExpression вернуло false, выполняется следующая итерация. Общее количество итераций равно размеру последовательности Expression.
Названия переменных в XQuery всегда начинаются со знака доллара. Объявлять переменные можно только в FLWOR-выражениях, кванторных выражениях (см. ниже), в прологе запроса и при объявлении параметров функций. SQL Server не поддерживает переменные в прологе запроса и функции, так что объявить переменную можно только в FLWOR-выражениях и кванторных выражениях.
В самом FLWOR-выражении переменную можно объявлять в разделе for и в разделе let. Так как раздел let не поддерживается в SQL Server, переменные можно объявлять только в разделе for. Примеры объявления переменных в настоящем FLWOR-выражении находятся в конце этого раздела.
FLWOR-выражение в SQL Server нельзя применять для связывания нескольких XML-документов, так как отсутствует функция doc, которая возвращает узел документа по заданному URI. Возможно, в финальной версии появится подобная функция или функция расширения, которая будет принимать значение первичного ключа для данной таблицы и имя XML-поля. Фактически же, текущая версия вообще не поддерживает узел документа.
Пожалуй, FLWOR-выражение в SQL Server можно применять только для фильтрации элементов последовательности и трансформации структуры XML-фрагмента. В следующем примере демонстрируется и то, и другое: сначала выполняется фильтрация, а затем конструирование последовательности узлов элемента b.
declare @xml xml set @xml = '<a>10</a>, <a>2</a>, <a>3</a>' select @xml::query(' for $a in /a where $a < 10 return <b>{$a/text()}</b> ') |
Результат:
<b>2</b><b>3</b> |
Выражения FLWOR могут быть вложенными. Например:
declare @xml xml set @xml = '<a>10</a>, <a>2</a>, <b>4</b>, <a>3</a>, <b>6</b>' select @xml::query(' {-- для каждого элемента а --} for $a in /a
{-- для каждого элемента b --} for $b in /b
{-- этот раздел return относится к внутреннему циклу for Поэтому исполняется 3 * 2 раз --} return <r> <expression>{$a/text()} * {$b/text()}</expression> <equals>{$a*$b}</equals> </r> ') |
Здесь во внешнем цикле производится перебор элементов а, а во внутреннем – элементов b, и все элементы a умножаются на все элементы b. Результат:
<r><expression>10 * 4</expression><equals>40</equals></r> <r><expression>10 * 6</expression><equals>60</equals></r> <r><expression>2 * 4</expression><equals>8</equals></r> <r><expression>2 * 6</expression><equals>12</equals></r> <r><expression>3 * 4</expression><equals>12</equals></r> <r><expression>3 * 6</expression><equals>18</equals></r> |
Вследствие того, что SQL Server очень неважно соответствует стандарту, у вас может сложиться обманчивое впечатление, что выражения FLWOR не так круты, как их рекламируют. Ниже я попробую продемонстрировать возможности FLWOR из последнего черновика стандарта.
В качестве исходных данных будем использовать небольшую базу данных по форумам RSDN из статьи [6], которая, допустим, находится в файле forums.xml:
<?xml version="1.0" encoding="windows-1251" ?> <rsdn> <forums date="09.01.03"> <forum name="WinAPI" totalposts="16688" description="Системное программирование"> <moderators/> <top-poster>Alex Fedotov</top-poster> </forum> <forum name="COM" totalposts="10116" description="Компонентные технологии"> <moderators/> <top-poster>Vi2</top-poster> </forum> <forum name="Delphi" totalposts="5001" description="Delphi и Builder"> <moderators> <moderator name="Sinclair"/> <moderator name="Hacker_Delphi"/> </moderators> <top-poster>Sinclair</top-poster> </forum> <forum name="DB" totalposts="6606" description="Базы данных"> <moderators> <moderator name="_MarlboroMan_"/> </moderators> <top-poster>Merle</top-poster> </forum> </forums> </rsdn> |
И базу данных пользователей, которая находится в файле users.xml:
<?xml version="1.0" encoding="windows-1251"?> <rsdn date="20/04/2004"> <users> <user nickname="Alexey Shirshov" name="Ширшов Алексей Николаевич" posts="4327" articles="16" rate="5779" total-rating="27"> <location from="Уфа" workin="Москва"/> </user> <user nickname="Alex Fedotov" name="Alex Fedotov" posts="2617" articles="11" rate="6885" total-rating="31"> <location from="" workin="San Francisco"/> </user> <user nickname="Vi2" name="Шарахов Виктор" posts="3094" articles="1" rate="5922" total-rating="26"> <location from="Ижевск" workin="Ижевск"/> </user> <user nickname="Sinclair" name="Антон Злыгостев" posts="4325" articles="5" rate="11136" total-rating="39"> <location from="Новосибирск" workin="Новосибирск"/> </user> <user nickname="Hacker_Delphi" name="Michael Polyudov" posts="2740" articles="2" rate="1880" total-rating="12"> <location from="Новосибирск" workin="Новосибирск"/> </user> <user nickname="_MarlboroMan_" name="Полюдов Дмитрий Петрович" posts="2104" articles="1" rate="2798" total-rating="13"> <location from="Новосибирск" workin="Новосибирск"/> </user> <user nickname="Merle" name="Ivan D. Bodiaguine" posts="2049" articles="5" rate="4974" total-rating="23"> <location from="Москва" workin="Москва"/> </user> </users> </rsdn> |
Выберем всех пользователей, которые являются модераторами форумов:
(: для каждого модератора :) for $moders in doc("forums.xml")/rsdn/forums/forum/moderators/moderator/@name
(: загружаем его профиль по имени :) let $user := doc("users.xml")/rsdn/users/user[@nickname = $moders]
(: и копируем его в результирующую последовательность :) return $user |
Теперь выберем всех пользователей, которые являются модераторами с количеством баллов больше среднего:
(: выбираем всех пользователей :) let $user := doc("users.xml")/rsdn/users/user
(: считаем средний бал для всех пользователей :) let $avg-rate := avg($user/@rate)
(: в цикле для каждого модератора :) for $moders in doc("forums.xml")/rsdn/forums/forum/moderators/moderator/@name
(: находим соответствующий ему профиль :) let $moder-user := $user[@nickname = $moders]
(: и фильтруем по баллам :) where $moder-user/@rate > $avg-rate
(: копируем модератора в результирующую последовательность :) return $moder-user |
Следующий запрос возвращает модифицированный элемент top-poster из файла forums.xml:
(: для каждого топ-постера :) for $top-poster in doc("forums.xml")/rsdn/forums/forum/top-poster
(: получаем его профиль :) let $user := doc("users.xml")/rsdn/users/user[@nickname = $top-poster] return
(: атрибут nick - имя пользователя :) <top-poster nick="{$top-poster}"> <name> { (: копируем атрибут name с использованием конструктора xs:string (имя пользователя в миру) :) xs:string($user/@name) }</name>
{ (: копируем элемент location из профиля пользователя :) $user/location }
<rating> { (: копируем значение атрибута total-rating :) $user/@total-rating }</rating> </top-poster> |
Результат:
<?xml version="1.0" encoding="UTF-8"?> <top-poster nick="Alex Fedotov"> <name>Alex Fedotov</name> <location from="" workin="San Francisco"/> <rating total-rating="31"/> </top-poster> <top-poster nick="Vi2"> <name>Шарахов Виктор</name> <location from="Ижевск" workin="Ижевск"/> <rating total-rating="26"/> </top-poster> <top-poster nick="Sinclair"> <name>Антон Злыгостев</name> <location from="Новосибирск" workin="Новосибирск"/> <rating total-rating="39"/> </top-poster> <top-poster nick="Merle"> <name>Ivan D. Bodiaguine</name> <location from="Москва" workin="Москва"/> <rating total-rating="23"/> </top-poster> |
И последний запрос: выберем сумму балов пользователей, проживающих в одном городе:
(: для каждого города :) for $cities in distinct-values(doc("users.xml")/rsdn/users/user/location/@workin)
(: найдем пользователей, в нем проживающих :) let $users-in-the-same-city := doc("users.xml")/rsdn/users/user[location/@workin = $cities]
(: отсортируем выходную последовательность в порядке, обратном сумме балов пользователей данного города :) order by sum($users-in-the-same-city/@rate) descending
(: сконструируем результат :) return <city name="{$cities}" total-rate="{sum($users-in-the-same-city/@rate)}" /> |
Вот результат этого запроса:
<?xml version="1.0" encoding="UTF-8"?> <city name="Новосибирск" total-rate="15814"/> <city name="Москва" total-rate="10753"/> <city name="San Francisco" total-rate="6885"/> <city name="Ижевск" total-rate="5922"/> |
Как видите, XQuery мало в чем уступает SQL, и даже, я уверен, значительно превосходит его по гибкости. Единственной проблемой остается производительность подобных запросов, но об этом поговорим в конце статьи.
