Запрос XML-данных при помощи языка XQuery (исходники)

Дон Чамберлин (Don Chamberlin), Синтия M. Саракко (Cynthia M. Saracco)

Возможно, вы уже слышали о новой архитектуре DB2 Viper, поддерживающей как табличную, так и иерархическую структуры данных. Действительно, в предыдущих статьях мы рассматривали новые функции DB2 для работы с XML-данными, рассказывали, как создавать объекты базы данных и заполнять их XML-данными и объясняли, как работать с XML-данными при помощи языков запросов SQL и SQL/XML. В этой статье мы продолжим изучение возможностей работы с XML-данными в программе DB2, фокусируясь, главным образом, на новой особенности программы - поддержке языка запросов XQuery.

В DB2 язык XQuery рассматривается как полноценный язык, что позволяет пользователям сразу записывать выражения XQuery, не требуя внедрения или встраивания выражений XQuery в оболочку предложений SQL. Более того, механизм запросов DB2 обрабатывает запросы XQuery в свойственной этому языку системе команд, это означает, что выражения XQuery анализируются, оцениваются и оптимизируются без скрытой от пользователя трансляции на язык SQL. Конечно, если вы захотите написать "двуязычный" запрос, который содержит одновременно выражения XQuery и SQL, DB2 обработает и оптимизирует и такие запросы.

Как и в статье по SQL/XML "Запрос XML-данных в среде DB2 при помощи языка SQL": в этой статье вы найдете обзор нескольких обычных для запросов задач и увидите, как можно использовать язык XQuery для решения этих задач. Но сначала вкратце рассмотрим отличия языка XQuery от SQL.

Язык XQuery

XQuery во многом отличается от SQL, главным образом, потому, что эти языки разрабатывались для работы с различными моделями данных, имеющими разную структуру. Документ в формате XML содержит иерархии, для него характерен внутренний порядок. Табличные структуры данных, поддерживаемые СУБД на базе SQL, являются однородными и линейными, строки, как таковые, неупорядочены.

Различия между этими моделями данных привели к нескольким принципиальным отличиям в соответствующих им языках запросов. Например, XQuery поддерживает выражения пути, что позволяет программистам перемещаться по иерархической структуре документа XML, в то время как "чистый" SQL (без расширений XML) не допускает такой возможности. XQuery поддерживает как типизированные, так и не типизированные данные, а для данных SQL всегда определен конкретный тип. В XQuery нет нулевых значений, поскольку в документах XML недостающие или неизвестные данные пропускаются, а SQL, как известно, использует нули для представления недостающих или неизвестных значений данных. XQuery возвращает последовательности данных XML; SQL возвращает наборы результатов, состоящие из различных типов данных SQL.

Это только некоторые из принципиальных различий между XQuery и SQL. В рамках этой вводной статьи не приводится исчерпывающий список таких отличий, но они будут подробно рассматриваться в следующем номере журнала IBM Systems Journal. А сейчас давайте перейдем к изучению некоторых основных аспектов языка XQuery и способов его использования для запроса XML-данных в программе DB2 Viper.

База данных для упражнений

Запросы, описанные в этой статье, обращаются к примерам таблиц, создание которых описано в статье "Начинаем работать с программой DB2 Viper". Коротко: листинг 1 определяет примеры таблиц "items" и "clients".

Листинг 1. Определения таблиц

				
create table items (
id 		int primary key not null, 
brandname 	varchar(30), 
itemname 	varchar(30), 
sku 		int, 
srp 		decimal(7,2), 
comments 	xml
)

create table clients(
id 		int primary key not null, 
name 		varchar(50), 
status 		varchar(10), 
contactinfo 	xml
)

Примеры XML-данных, включенных в столбец "items comments", показаны на рисунке 1, а примеры XML- данных, включенных в столбец "clients contactinfo" - на рисунке 2. Последующие примеры запросов будут ссылаться на конкретные элементы одного или сразу обоих документов XML.

Рисунок 1. Пример документа XML сохранен в столбце "comments" таблицы "items"
Пример документа XML сохранен в столбце "comments" таблицы "items"

Рисунок 2. Пример документа XML сохранен в столбце "contactinfo" таблицы "clients"
Пример документа XML сохранен в столбце

Среда запроса

Все запросы в этой статье разработаны для того, чтобы вы могли повторить их на своем компьютере. Это можно сделать через обработчик командной строки программы DB2 или редактор команд DB2 Command Editor модуля DB2 Control Center. Изображения снимков экрана и инструкции в данной статье относятся ко второму варианту (DB2 Viper поставляется со средой разработчика Developer Workbench на базе платформы Eclipse, что может помочь программисту конструировать запросы наглядным способом. В этой статье проблемы разработки приложений в среде разработки Developer Workbench не рассматриваются).

Для использования редактора команд DB2 Command Editor, откройте Control Center и выберите команду Tools -> Command Editor. На экране появится окно, показанное на рисунке 3 . В верхней панели введите запрос, затем нажмите зеленую стрелку в верхнем левом углу, чтобы выполнить его, и просмотрите полученные результаты в нижней панели или на отдельной вкладке "Query Results".

Рисунок 3. Окно DB2 Command Editor, которое может быть вызвано из DB2 Control Center
Окно DB2 Command Editor, которое может быть вызвано из DB2 Control Center

Примеры запросов XQuery

Как и в статье "Запрос XML-данных в среде DB2 при помощи языка SQL": здесь мы рассмотрим несколько распространенных бизнес-сценариев и продемонстрируем, как использовать язык XQuery, чтобы запрос соответствовал требованиям для XML-данных. В статье будут также рассмотрены более сложные ситуации, которые потребуют внедрения в запрос XQuery кода SQL.

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

В этой статье описываются два важных типа выражений XQuery: это выражения "FLWOR" и выражения пути. Выражения FLWOR подобны выражениям SELECT-FROM-WHERE в SQL - они используются для выполнения итераций по списку объектов и необязательного возврата значений, вычисленных для каждого объекта. Выражения пути, с другой стороны, перемещаются по иерархии XML-элементов и возвращают те элементы, которые обнаружены в конце пути.

Подобно выражению SELECT-FROM-WHERE языка запросов SQL, выражение XQuery FLWOR может включать несколько предложений, которые начинаются с определенных ключевых слов. Для начала предложений в выражении FLWOR используются следующие ключевые слова:

  • for: выполняет итерацию над последовательностью введенных значений, по очереди связывая переменную с каждым значением
  • let: задает переменную и присваивает ей значение, которое может быть списком, содержащим несколько элементов
  • where: определяет критерии фильтрации результатов запроса
  • order by: определяет порядок сортировки результатов
  • return: определяет результат, который следует отобразить

Выражение пути в XQuery состоит из серии "шагов", разделенных символом "косой черты". В простейшей форме, каждый шаг ведет вниз по иерархии XML, чтобы найти потомка для элемента, возвращенного на предыдущем шаге. Каждый шаг в выражении пути может также содержать предикат, который фильтрует элементы, возвращенные на этом шаге, оставляя только те из них, которые удовлетворяют некоторым условиям. Например, представим, что переменная $clients ограничена списком XML-документов, содержащих элементы <Client>, тогда четырехшаговое выражение пути $clients/Client/Address[state = "CA"]/zip возвратит список почтовых индексов клиентов, имеющих калифорнийские адреса.

В большинстве случаев можно написать запрос, используя одно из двух выражений - либо выражение FLWOR, либо выражение пути.

Использование в DB2 XQuery в качестве основного языка запросов

Для выполнения запросов XQuery непосредственно в среде DB2 Viper (в отличие от внедрения их в предложения SQL) следует предпослать запросу ключевое слово xquery. Это сообщает DB2 о необходимости вызвать для обработки запроса анализатор языка XQuery. Обратите внимание, что это нужно делать только в том случае, если вы используете XQuery в качестве верхнеуровневого языка запросов. Если вы включаете выражения XQuery в SQL, нет необходимости предварять их ключевым словом xquery. В этой статье язык XQuery используется в качестве основного языка, поэтому все запросы начинаются с xquery.

При выполнении в качестве основного языка запросов, XQuery требуется источник данных для ввода. Один из методов, который XQuery может использовать, чтобы получить данные для ввода, это вызов функции db2-fn:xmlcolumn с параметром, который определяет имя таблицы и имя столбца в столбце XML таблицы DB2. Функция db2-fn:xmlcolumn возвращает последовательность документов XML, которая хранится в данном столбце. Например, представленный в следующем листинге запрос возвращает последовательность документов XML, содержащих контактную информацию клиентов:

Листинг 2. Простой запрос XQuery, возвращающий контактные данные

						   
xquery db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')

Вспомните нашу схему базы данных (см. раздел "База данных для упражнений" ): мы сохраняли такие XML-документы в столбце "contactinfo" таблицы "clients". Обратите внимание, что имена столбца и таблицы в данном случае отображаются в верхнем регистре. Это объясняется тем, что имена таблицы и столбца перед записью во внутренний каталог DB2 обычно переводятся в верхний регистр. Язык XQuery является регистрозависимым, поэтому имена таблиц и столбцов, записанные в нижнем регистре, не соответствовали бы именам в каталоге DB2, записанным в верхнем регистре.

Извлечение определенных элементов XML

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

Листинг 3. Поиск данных о номере факса клиента при помощи выражения FLWOR

				
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
return $y

В первой строке листинга содержится указание для DB2 о необходимости вызова анализатора языка XQuery. В следующей строке DB2 получает указание выполнить итерацию на вложенных элементах элементов Client, содержащимся в столбце CLIENTS.CONTACTINFO. Каждый элемент fax по очереди связывается с переменной $y. Третья строка показывает, что для каждой итерации возвращается значение переменной $y. Результат представляет собой последовательность элементов XML, показанную в листинге 4:

Листинг 4. Пример вывода данных для предыдущего запроса

				
<fax>4081112222</fax>
<fax>5559998888</fax>     

Отступив от темы, заметим, что вывод может также содержать некоторую информацию, не представляющую большого интереса для данной статьи: версия XML и кодировка данных, например, <?xml version="1.0" encoding="windows-1252" ?>, и информация о пространстве имен XML, например, <fax xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">. Чтобы облегчить изучение результата запроса, в этой статье мы опускаем данную информацию. Тем не менее, она может быть важной для некоторых приложений XML. Если для выполнения запроса вы пользуетесь обработчиком командной строки программы DB2, вы можете использовать параметр -d, чтобы скрыть информацию описания XML и параметр -i для вывода результатов на печать в удобном виде.

Запрос из листинга 3 мог бы выглядеть чуть более лаконичным, если бы был решен в форме трехшагового выражения пути, как показано в листинге 5:

Листинг 5. Поиск данных о номере факса клиента при помощи выражения пути

				
 
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax

Первым шагом выражения пути осуществляется вызов функции db2-fn:xmlcolumn, что позволяет получить список XML-документов из столбца CONTACTINFO таблицы CLIENTS. Второй шаг возвращает все элементы Client данного документа, а третий шаг - элементы fax, вложенные в элементы Client.

Если вы не заинтересованы в получении по запросу кода XML-фрагментов, и вам нужно только текстовое представление выбранных значений элемента XML, то вы можете включить в предложение return функцию text (), как показано в листинге 6: Листинг 6:

Листинг 6. Два запроса на выборку текстового представления данных о номере факса клиента

				
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
return $y/text()

(or)

xquery
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax/text()

Вывод этих запросов может быть примерно таким, как показано в листинге 7:

Листинг 7. Пример вывода возвращаемых данных для предыдущего запроса

				
4081112222
5559998888

Результаты этих учебных запросов относительно просты, потому что элемент fax имеет в основе примитивный тип данных. Безусловно, в основе элементов могут быть и сложные типы, то есть, элементы могут содержать вложенные элементы (или вложенные иерархии). Элемент Address из контактной информации нашего клиента - один из примеров таких данных. В соответствии со схемой, которая приводится в статье "Начинаем работать с программой DB2 Viper", этот элемент может содержать название улицы, номер дома и квартиры, название города, штата и почтовый индекс. Подумаем, какие данные возвращает запрос XQuery в (листинг 8) :

Листинг 8. Запрос на выборку сложного типа данных XML при помощи выражения FLWOR

				
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
return $y

Если вы решили, что правильный ответ - это последовательность фрагментов XML-данных, содержащая элементы Address и все вложенные элементы, то вы не ошиблись В листинге 9 - пример такого вывода данных:

Листинг 9. Пример вывода возвращаемых данных для предыдущего запроса

				
<Address>
  <street>5401 Julio Ave.</street>
  <city>San Jose</city>
  <state>CA</state>
  <zip>95116</zip>
</Address>
. . .  
<Address>
  <street>1204 Meridian Ave.</street>
  <apt>4A</apt>
  <city>San Jose</city>
  <state>CA</state>
  <zip>95124</zip>
</Address>  

Примечание: Данный пример представлен в форматированном виде, чтобы облегчить его чтение. Редактор команд DB2 Command Editor отображает каждую запись адреса клиента отдельной строкой.

Фильтрация значений элементов XML

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

Можно предположить, что предложение where запросов XQuery позволяет выполнить фильтрацию результатов по значению элемента zip в нашем XML-документе. Листинг 10 показывает, как добавить предложение where к предыдущему выражению FLWOR (листинг 8) и получить только те адреса, которые нас интересуют:

Листинг 10. Выражение FLWOR с новым предложением "where"

						   
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
where $y/zip="95116"
return $y

Добавленное выражение where достаточно просто для понимания. Предложение for связывает переменную $y с каждым возвращаемым адресом по очереди. Предложение where содержит небольшое выражение пути, которое перемещает фокус запроса от каждого элемента address к вложенному в него элементу zip. Предложение where истинно (и адрес остается в результатах запроса) только в том случае, если значение элемента zip равно 95116.

Такой же результат можно получить, добавив к выражению пути предикат, как показано в листинге 11:

Листинг 11. Выражение пути, в которое добавлен фильтрующий предикат

				
xquery
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address[zip="95116"]

Конечно, вы можете указать в качестве условия фильтрации значение почтового индекса, и тогда будут возвращены элементы независимо от названия улицы и номера дома. Более того, вы также можете выполнить фильтрацию по нескольким значениям элементов XML в одном запросе. Следующий запрос возвращает информацию об адресах электронной почты тех клиентов, чей адрес соответствует конкретному почтовому индексу Нью-Йорка (10011) или любых клиентов проживающих в городе Сан-Хосе.

Листинг 12. Фильтрация результатов запроса по нескольким элементам XML при помощи выражения FLWOR

				
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address/zip="10011" or $y/Address/city="San Jose"
return $y/email

Обратите внимание, что мы изменили предложение for, и теперь оно связывает переменную $y не с элементами Address, а с элементами Client. Это дает возможность отфильтровать элементы Client по одной части поддерева (Address) и возвратить другую часть поддерева (email). Выражение пути в предложении where и предложение return могут быть написаны для элемента, который связывается с переменной (в нашем случае, $y).

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

Листинг 13. Фильтрация результатов запроса по нескольким элементам XML при помощи выражения пути

				
xquery 
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client[Address/zip="10011"  
or Address/city="San Jose"]/email;

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

  1. Вы не получите XML-данные на тех отвечающих критериям запроса клиентов, которые не предоставили свой адрес электронной почты. Другими словами, если у вас есть 1000 клиентов, проживающих в Сан-Хосе или имеющих почтовый индекс 10011, а 700 из них предоставили по одному адресу электронной почты, вы получите результат в виде списка этих 700 адресов электронной почты. Причина этого кроется в принципиальном различии между языками XQuery и SQL, о котором мы говорили раньше - Xquery не использует нулевые значения.
  2. Вы не узнаете, какие из адресов электронной почты были извлечены из одного документа XML. Другими словами, если у вас есть 700 клиентов, проживающих в Сан-Хосе или имеющих почтовый индекс 10011, и каждый из них указал по два адреса электронной почты, вы получите результат в виде списка этих 1400 адресов электронной почты. Вы не могли бы получить последовательность из 700 записей, каждая из которых включает два электронных адреса.

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

Существует несколько способов переделать запрос таким образом, чтобы он возвратил результаты, представляющие собой смешанную информацию в одной форме, c указанием тех случаев, когда несколько адресов электронной почты были извлечены из одной записи о клиенте (то есть, из одного документа XML). Давайте ознакомимся с одним из способов. Но если все, чего вы хотите - это получить список, содержащий один адрес электронной почты для каждого соответствующего критериям запроса клиента, вам следует лишь слегка изменить предложение return в предыдущем запросе:

Листинг 14. Извлечение только первого элемента email для каждого клиента

				
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address/zip="10011" or $y/Address/city="San Jose"
return $y/email[1]

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

Преобразование вывода XML

Замечательная особенность XQuery - это возможность преобразовать XML-результат запроса из одной формы XML в другую. Например, вы можете использовать запрос XQuery для выборки всех или части хранимых документов XML и преобразовать вывод в формат HTML, который можно просмотреть в веб-браузере. Следующий запрос (листинг 15) осуществляет выборку адресов клиентов с сортировкой результатов по почтовому индексу, и преобразует результат в элементы XML, являющиеся частью неупорядоченного списка HTML:

Листинг 15. Выполнение запроса к DB2 на выборку XML-данных и возвращение результата в формате HTML

				
xquery 
<ul> {
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
order by $y/zip
return <li>{$y}</li> 
} </ul>

Запрос начинается достаточно просто с ключевого слова xquery, которое сообщает синтаксическому анализатору DB2, что язык XQuery используется в качестве основного языка. Вторая строка указывает, что в результаты следует включить HTML-разметку для неупорядоченного списка (<ul>). Эта же строка вводит фигурную скобку, первую из двух комплектов, используемых в этом запросе. Фигурные скобки заставляют DB2 проанализировать и обработать вложенное выражение, а не рассматривать его как строку символов..

Третья строка выполняет итерацию на последовательности адресов клиентов, связывая переменную $y по очереди с каждым элементом address. Четвертая строка включает новое предложение order by, определяющее, что результаты должны быть возвращены в порядке возрастания (это порядок по умолчанию) почтовых индексов клиентов (вложенного элемента zip для каждого адреса, связанного с $y). Предложение return сообщает программе, что элементы Address следует заключить в HTML-тэги элементов списка до возвращения результата. А заключительная строка заканчивает формирование запроса и завершает HTML-разметку неупорядоченного списка.

Вывод будет отображен так, как показано в листинге 16:

Листинг 16. Пример HTML-вывода для предыдущего запроса

				
<ul>
  <li>
     <Address>
         <street>9407 Los Gatos Blvd.</street>
         <city>Los Gatos</city>
         <state>CA</state>
         <zip>95032</zip>
     </Address>
  </li>
  <li>
     <Address>
         <street>4209 El Camino Real</street>
         <city>Mountain View</city>
         <state>CA</state>
        <zip>95033</zip>
     </Address>
  </li>
. . .  
</ul>

Давайте обдумаем тему, которая обсуждалась раньше: как написать запрос XQuery, чтобы в возвращенных результатах были показаны пропущенные значения и отмечены случаи, когда один XML-документ (например, одна запись о клиенте) содержит повторяющиеся элементы (такие, как несколько адресов электронной почты). Один из способов - включить возвращаемые данные в новый элемент XML, как показано в следующем запросе в листинге 17:

Листинг 17. Отображение недостающих значений и повторяющихся элементов в результатах запроса XQuery

				
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address[zip="10011"] or $y/Address[city="San Jose"]
return <emailList> {$y/email} </emailList>

Выполнив этот запрос, мы получим возвращенные данные в виде последовательности элементов "emailList", причем один элемент будет соответствовать одной записи о клиенте. Каждый элемент emailList будет содержать адрес электронной почты. Если DB2 обнаружит в записи клиента единственный адрес электронной почты, то будет возвращен этот элемент и его значение. При обнаружении несколько адресов будут возвращены все элементы e-mail вместе с их значениями. Наконец, если не будет найдено ни одного адреса электронной почты, то будет возвращен пустой элемент emailList. Таким образом, вывод может выглядеть следующим образом:

Листинг 18. Пример вывода для предыдущего запроса

				
<emailList>
   <email>love2shop@yahoo.com</email>
</emailList>
<emailList/>
<emailList>
   <email>beatlesfan36@hotmail.com</email>
   <email>lennonfan36@hotmail.com</email>
</emailList>       
. . . 

Использование условной логики

Способность XQuery трансформировать результаты запроса XML можно для снижения сложности кода приложения комбинировать с встроенной в язык поддержкой условной логики . Давайте рассмотрим простой пример. Таблица "items" содержит столбцы XML с комментариями, сделанными клиентами по поводу продукции. Для клиента, который запросил ответ на свои комментарии, вам, возможно, захочется создать новые элементы "action", содержащие идентификатор продукта, идентификатор клиента и сообщение, чтобы эту информацию можно было отправить соответствующему лицу для обработки. Однако комментарии, не требующие ответа, тем не менее, имеют значение для бизнеса, и вы не хотели бы просто игнорировать их. Для этих комментариев создайте элемент "info", включающий только идентификатор продукта и сообщение. В следующем листинге показано, как можно для решения этой задачи использовать выражение XQuery if-then-else:

Листинг 19. Использование выражения "if-then-else" в запросе XQuery

				
xquery 
for $y in db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment 
return ( 
	if ($y/ResponseRequested = 'Yes') 
		then <action>
			{$y/ProductID, 
			 $y/CustomerID, 
			 $y/Message}
		      </action>
		else ( <info>
			{$y/ProductID, 
			 $y/Message}
			</info>
		)
)

Большая часть аспектов этого запроса должна быть понятна вам уже сейчас, так что давайте сконцентрируемся на условной логике. Предложение if определяет, будет ли значение вложенного элемента ResponseRequested для данного комментария равным "Да".Если будет, то вычисляется предложение then , в результате чего DB2 возвращает новый элемент ("action"), который содержит три вложенных элемента: В противном случае, вычисляется предложение else, и DB2 возвращает элемент "info", который содержит только идентификатор продукта и сообщение.

Использование предложения "let"

Мы уже рассмотрели как использовать все части выражения FLWOR кроме одной: речь идет о предложении let. Это предложение используется для присваивания значения (возможно, содержащего список из нескольких объектов) переменной, которая может быть использована в других предложениях выражения FLWOR.

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

Листинг 20. Использование предложения "let"

				
xquery
for $p in distinct-values
     (db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment/ProductID)
let $pc := db2-fn:xmlcolumn('ITEMS.COMMENTS')
        /Comments/Comment[ProductID = $p]
return
   <product>
          <id> { $p } </id> 
          <comments> { count($pc) } </comments>
   </product>

Функция distinct-values в предложении for возвращает список всех неповторяющихся значений элементов ProductID, найденных среди комментариев в столбце COMMENTS таблицы ITEMS. Предложение for связывает переменную $p по очереди с каждым из этих значений ProductID. Для каждого значения $p предложение let повторно сканирует столбец ITEMS и связывает переменную $pc со списком, содержащим все комментарии, для которых ProductID совпадает с ProductID в переменной $p. Предложение return конструирует новый элемент "product" для каждого из неповторяющихся значений ProductID. Каждый из этих элементов "product" содержит два вложенных элемента: элемент "id", содержащий значение ProductID, и элемент "comments", содержащий число, соответствующее количеству комментариев, полученных по данному продукту.

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

Листинг 21. Пример вывода для предыдущего запроса

						   
<product>
     <id>3926</id>
     <comments>28</comments>
</product>
<product>
      <id>4097</id>
      <comments>13</comments>
</product>

Запросы XQuery c вложенным SQL

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

Конечно, весь язык XQuery нельзя изучить, прочитав такую короткую статью. Но мы не можем пропустить обширную тему, которую до сих пор не рассматривали: как встроить код SQL в запрос XQuery. Это может оказаться полезным, если вам нужно составить запросы, которые осуществляют фильтрацию данных по XML- и не-XML значениям столбцов.

Вспомните статью "Запрос XML-данных в среде DB2 при помощи языка SQL": в ней шла речь о том, как использовать для решения этой задачи простые выражения XQuery, встроенные в предложения SQL. В этой статье попробуем сделать наоборот: встроим в запрос на языке XQuery код SQL, чтобы ограничить результат по значениям как в традиционном формате данных SQL, так и в специфическом формате элементов XML.

Вместо того, чтобы вызвать функцию db2-fn:xmlcolumn, которая возвращает XML-данные из столбца таблицы, можно вызвать функцию db2-fn:sqlquery, которая выполнит запрос SQL и возвратит только выбранные данные. Запрос SQL, переданный функции db2-fn:sqlquery, должен возвратить XML-данные. Обработка этих XML-данных впоследствии может быть продолжена в среде XQuery.

Запрос, описанный в листинге 22 , осуществляет поиск информации о комментариях, относящихся к продуктам с рекомендуемой розничной ценой (*srp*) более 100 долларов вместе с запросом ответа от клиента. Вспомним, что данные о цене хранятся в десятичном столбце в формате SQL, тогда как комментарии клиентов хранятся как XML-данные. Возвращенные данные, включая идентификатор продукта, идентификатор клиента и сообщение клиента для каждого соответствующего критерию запроса комментария, хранящегося в базе данных, включается в отдельный элемент XML "action".

Листинг 22. Встраивание кода SQL в запрос XQuery

				
xquery 
for $y in 
db2-fn:sqlquery('select comments from items where srp > 100')/Comments/Comment 
where $y/ResponseRequested="Yes"
return (
   <action>
          {$y/ProductID, 
           $y/CustomerID, 
           $y/Message}
  </action>
)

И снова, большая часть аспектов этого запроса должна быть понятна вам уже сейчас, так что давайте сконцентрируемся на новой функции. db2-fn:sqlquery. DB2 обрабатывает предложение SQL SELECT, дополненное этой функцией, чтобы определить, в каких строках содержится информация об объектах стоимостью более 100 долларов. Документы, хранимые в этих строках, служат вводными данными для выражения пути, которое возвращает все вложенные элементы Comments. Последующие части запроса используют предложение XQuery where для дальнейшей фильтрации возвращаемых данных и преобразования частей выбранных комментариев в новые фрагменты XML.

С учетом вышесказанного, давайте подумаем, как можно решить другую задачу, которая немного отличается от первой. Представьте, что вам нужно получить список адресов электронной почты для "особо ценных" клиентов, проживающих в городе Сан-Хосе.Кроме того, если для одного клиента в базе данных имеется несколько адресов электронной почты, то вы хотите включить их все в вывод запроса таким образом, чтобы они были частью отдельной записи о клиенте.. И, наконец, если соответствующий критериям отбора "особо ценный" клиент не предоставил адреса электронной почты, вы хотите получить его обычный почтовый адрес. Листинг 23 иллюстрирует один из способов составления такого запроса:

Листинг 23. Встраивание кода SQL в тело запроса XQuery, который содержит условную логику

				
xquery 
for $y in 
db2-fn:sqlquery('select contactinfo from clients where status=''Gold'' ')/Client
where $y/Address/city="San Jose"
return (
     if ($y/email) then <emailList>{$y/email}</emailList>
     else $y/Address   
)

Два аспекта этого запроса нуждаются в некотором объяснении. Во-первых, предложение SELECT, встроенное во вторую строку, содержит предикат запроса по столбцу "status", который сравнивает значения столбца VARCHAR со строкой "Gold". В SQL такие строки заключаются в простые кавычки. Обратите внимание на то, что, хотя кажется, что в примере используются двойные кавычки, на самом деле это две простые кавычки до и после критерия сравнения ("Gold"). Дополнительные простые кавычки являются escape-символами. Если вы заключите ваш предикат запроса со строковым условием вместо пары простых кавычек в двойные кавычки, вы получите синтаксическую ошибку.

Кроме того, предложение return в этом запросе содержит условную логику, которая позволяет определить, имеется ли элемент email в данной записи о клиенте. Если имеется, то запрос возвратит новый элемент "emailList", содержащий все адреса электронной почты клиента (то есть, все элементы e-mail для этого клиента). В противном случае будет возвращен почтовый адрес клиента (то есть, элемент Address для этого клиента).

Индексирование

И наконец, ничего не стоит научиться создавать специализированные индексы XML для ускорения доступа к данным, хранимым в столбцах XML. Мы не будем рассматривать эту тему в данной статье из-за вводного характера статьи и малого объема данных для упражнений. Однако, в производственной обстановке определение соответствующих индексов может быть критически важным для достижения оптимальной производительности. Чтобы Ресурсы больше узнать о новой технологии индексирования в DB2, обратитесь к источникам из раздела "Ресурсы".

Заключение

Язык XQuery имеет много существенных отличий от языка SQL; некоторые из них были освещены в этой статье. Более детальное изучение этого языка поможет вам определить, в каких случаях можно с выгодой использовать его в работе, а также понять, когда может оказаться полезным совместное использование XQuery и SQL. В следующей статье мы углубимся в еще одну тему, которая может показаться вам интересной: как разработать Java-приложения, использующие возможности работы с XML в DB2. Впрочем, и в этой статье содержится маленький пример, как можно внедрить в запрос XQuery Java-приложение.


Страница сайта http://185.71.96.61
Оригинал находится по адресу http://185.71.96.61/home.asp?artId=16395