pureXML в DB2 9: Каким способом запрашивать XML-данные? (исходники)
Маттиас Никола
Встроенная поддержка XML в DB2 предлагает эффективные и разносторонние возможности для управления XML-данными. DB2 хранит и обрабатывает XML в собственном иерархическом формате, избегая ограничений по производительности и гибкости, имеющихся при записи XML в виде текста в CLOB-полях или при отображении в реляционные таблицы. В отличие от баз данных, работающих только с XML-данными, DB2 V9 обеспечивает также плавную интеграцию реляционных данных и XML-данных в одной базе данных, даже в одной строке таблицы. Эта гибкость отображается в поддержке языка, что позволяет обращаться к реляционным данным, XML-данным или обоим типам одновременно. XML-данные можно запрашивать любым из следующих четырех способов:
- Обычный SQL (XQuery не используется)
- SQL/XML, то есть, XQuery, встроенный в SQL
- XQuery как автономный язык (SQL не используется)
- XQuery со встроенным SQL
Введение в запросы XML-данных с использованием XQuery и SQL/XML уже публиковалось в статьях: "Запрос DB2 XML-данных с использованием SQL" и "Запрос DB2 XML-данных с использованием XQuery". Мы предполагаем, что вы знакомы с концепциями, представленными в этих двух статьях. Обратите внимание на то, что XPath - это подмножество языка XQuery, поэтому он неявным образом присутствует везде, где мы упоминаем XQuery. Возможно, вы уже знаете XPath, если использовали таблицы стилей XSLT или пути к месторасположению в DB2 XML Extender. Во многих ситуациях языка XPath достаточно для извлечения XML-значений или выражения XML-предикатов, поэтому вы можете начать работать с XPath, даже если еще не знакомы со всеми дополнительными функциональными возможностями XQuery.
DB2 позволяет использовать все эти варианты для максимизации производительности и адаптации запросов данных к требованиям ваших приложений. В данной статье мы рассматриваем следующие вопросы:
- Каковы основные характеристики, преимущества и недостатки этих четырех вариантов?
- Какой вариант и в какой ситуации нужно использовать?
Давайте начнем с резюме, а затем более подробно рассмотрим каждый вариант и специфические примеры.
Множество запросов можно записать на простом XQuery, SQL/XML или XQuery со встроенным SQL. В определенных случаях один из вариантов является интуитивно более подходящим для выражения логики приложения, чем остальные. В общем случае "правильный" подход к запросу XML-данных должен выбираться для конкретной ситуации с учетом требований приложения и его характеристик. Однако мы можем высказать следующие рекомендации.
- Простой SQL без XQuery и XPath действительно полезен только для извлечения документов полностью и для таких операций как вставка, удаление и обновление документов в целом. Выбор документов должен основываться на столбцах этой же таблицы, отличных от XML.
- SQL/XML с XQuery или XPath, встроенными в SQL-запросы, обеспечивает более широкую функциональность и меньшие ограничения. Можно выражать предикаты по XML-столбцам, извлекать фрагменты документов, передавать маркеры параметров (parameter markers) в XQuery-выражения, использовать полнотекстовый поиск, объединение (aggregation) и группировку на SQL-уровне, а также комбинировать и подключать XML к реляционным данным гибким способом. Данный вариант хорошо подходит большинству приложений. Даже если вам не нужны все эти преимущества прямо сейчас, все равно можно выбрать этот подход, чтобы обеспечить возможность будущих расширений.
- XQuery - это мощный язык запросов, специально разработанный для запроса XML-данных. Поэтому такой вариант хорош тогда, когда приложению требуется запросить и управлять только XML-данными, не обращая внимания на реляционные данные. Это иногда проще и интуитивно понятнее. Кроме того, при миграции с базы данных, поддерживающей только XML, на DB2 9 и при наличии уже существующих XQuerie-запросов, предпочтительнее использовать простой XQuery.
- XQuery со встроенным SQL может быть хорошим вариантом, когда желательно использовать реляционные предикаты и индексы, а также полнотекстовый поиск для предварительной фильтрации документов из XML-столбца, которые затем являются входными данными для XQuery. SQL, встроенный в XQuery, позволяет также выполнять внешние функции с XML-столбцами. Но если нужно выполнить запросы для анализа данных с использованием группировок и объединений, предпочтительнее, возможно, использовать SQL/XML.
Не зависимо от выбранной комбинации SQL и XQuery в одном выражении, DB2 использует единый гибридный компилятор для формирования и оптимизации единого плана выполнения для всего запроса без ущерба для производительности выполнения запроса.
В следующей таблице представлены преимущества, свойственные четырем вариантам запроса XML-данных.
Таблица 1. Резюме
|
Простой SQL |
SQL/XML |
Простой XQuery |
XQuery со встроенным SQL/XML |
XML-предикаты |
- |
++ |
++ |
++ |
Реляционные предикаты |
++ |
++ |
- |
+ |
XML и реляционные предикаты |
- |
++ |
- |
++ |
Объединение XML с реляционными данными |
- |
++ |
- |
++ |
Объединение XML с XML |
- |
+ |
++ |
++ |
Преобразование XML-данных |
- |
o |
++ |
++ |
Вставка, обновление, удаление |
++ |
++ |
- |
- |
Маркеры параметров |
+ |
++ |
- |
- |
Полнотекстовый поиск |
+ |
++ |
- |
++ |
Объединение и группировка |
- |
++ |
o |
o |
Вызовы функций |
++ |
++ |
- |
++ |
В приведенной выше таблице "-" обозначает, что данный язык не поддерживает эту функциональность, "+" означает, что функциональность поддерживается, но может существовать более эффективный или удобный способ, "++" обозначает, что данный язык очень хорошо подходит для выражения указанной функциональности, и, наконец, "o" обозначает, что, хотя функциональность и может быть выражена, но это делается неуклюже или не эффективно.
Теперь давайте определим некоторые примерные данные и таблицы, для того чтобы можно было рассмотреть конкретные примеры запросов.
Для обсуждения вариантов запросов XML-данных мы используем три таблицы, приведенные ниже. Таблица dept имеет два столбца с названиями unitID и deptdoc. Каждая строка таблицы dept описывает один отдел вымышленной компании. Столбец unitID идентифицирует подразделение, которому принадлежит отдел (подразделение может иметь несколько отделов), а столбец deptdoc содержит XML-документ, перечисляющий сотрудников отдела. Таблица project имеет один столбец projectDoc с типом XML. Каждая строка таблицы projectDoc содержит XML-документ, описывающий конкретный проект. Проект может охватывать более одного отдела. Для иллюстрации гибридных реляционных и XML-запросов, а также соединений (joins), имеется чистая реляционная таблица unit, в которой содержится фамилия, менеджер и т.д. для каждого подразделения. Подразделение может иметь несколько отделов.
create table dept( unitID char(8), deptdoc xml) |
unitID |
deptdoc |
WWPR |
<dept deptID="PR27">
<employee id="901">
<name>Jim Qu</name>
<phone>408 555 1212</phone>
</employee>
<employee id="902">
<name>Peter Pan</name>
<office>216</office>
</employee>
</dept>
|
|
WWPR |
<dept deptID="V15">
<employee id="673">
<name>Matt Foreman</name>
<phone>416 891 7301</phone>
<poffice>216</office>
</employee>
<description>This dept supports sales world wide</description>
</dept>
|
|
S-USE |
... |
... |
... |
create table project(projectDoc xml) |
projectDOC |
<project ID="P0001">
<name>Hello World</name>
<deptID>PR27</deptID>
<manager>Peter Pan</manager>
</project>
|
|
<project ID="P0009">
<name>New Horizon</name>
<deptID>PR27</deptID>
<deptID>V15</deptID>
<manager>Matt Foreman</manager>
<description>This project is brand new</description>
</project>
|
|
create table unit( unitID char(8) primary key not null, name char(20), manager varchar(20),...)
/-------10--------20--------30--------40--------50--------60--------70--------80--------9/
/-------- XML error: The previous line is longer than the max of 90 characters ---------/
|
unitID |
name |
manager |
... |
WWPR |
Worldwide Marketing |
Jim Qu |
... |
S-USE |
Sales US East Coast |
Tom Jones |
... |
... |
... |
... |
... |
Простой SQL может использоваться без применения XPath или XQuery для чтения полных документов без XML-предикатов. Это очень удобно, когда приложение может идентифицировать XML-документы для извлечения документов полностью на основе реляционных предикатов для одной и той же таблицы. Например, Query 1 извлекает все документы об отделах для подразделения "WWPR".
Query 1
select deptdoc
from dept
where unitID = 'WWPR'; |
Аналогично, Query 2 возвращает XML-данные для всех отделов, менеджером которых является Jim Qu.
Query 2
select deptdoc
from dept d, unit u
where d.unitID = u.unitID and u.manager= 'Jim Qu'; |
Очевидным недостатком является невозможность указания предикатов по самим XML-данным или извлечения фрагментов XML-документа. В нашем примере простого SQL недостаточно для выборки только отдела PR27 и возврата фамилий сотрудников.
Если запросы не требуют предикатов по XML-столбцу и всегда возвращают XML-документы полностью, простого SQL может быть достаточно. В этом случае можно было бы хранить XML просто в столбцах VARCHAR или CLOB, повышая производительность операций вставки и извлечения документов полностью.
Без использования какой-либо поддержки SQL/XML или XQuery в DB2 V9 простой SQL все равно позволяет записывать в запросах условия полнотекстового поиска. При помощи DB2 Net Search Extender можно создать полнотекстовые индексы по XML-столбцам для поддержки поиска текста по базовому ключевому слову вплоть до расширенного поиска с морфологией, словарем и нечетким поиском (fuzzy search) на 37 языках. Можно также ограничить текстовый поиск до определенных разделов документа, определенных выражениями path. Основываясь на поиске по текстовым индексам, Query 3 возвращает все документы отделов, содержащие строку "sales" в любом месте документа в /dept/description.
Query 3
select deptdoc
from dept
where CONTAINS(deptdoc,'SECTION("/dept/description") "sales" ')=1; |
SQL/XML - это часть стандарта языка SQL, определяющая новый тип данных (XML) вместе с функциями запроса, формирования, проверки корректности и преобразования XML-данных. DB2 V8 уже имела многочисленные функции публикации SQL/XML, и пользователи могут формировать XML из реляционных данных, используя XMLELEMENT, XMLATTRIBUTE, XMLFOREST, XMLAGG, а также другие функции.
DB2 9 добавляет новые функции SQL/XML-запросов, включая предикаты XMLQUERY, XMLTABLE и XMLEXISTS. Эти функции позволяют пользователям встраивать XQuery или простые XPath-выражения в SQL-запросы.
Как показано в Query 4, функция XMLQUERY обычно используется в операторе select для извлечения XML-фрагментов из XML-столбцов, тогда как XMLEXISTS обычно используется в операторе where для записи предикатов над XML-данными.
Query 4
select unitID, XMLQUERY('for $e in $d/dept/employee return $e/name/text()'
passing d.deptdoc as "d")
from dept d
where unitID LIKE 'WW%' and
XMLEXISTS('$d/dept[@deptID = "V15"]' passing d.deptdoc as "d"); |
Этот пример запроса использует XMLEXISTS для выбора отдела V15 подразделения "WW" и применяет XMLQUERY для возврата фамилий всех сотрудников из документа для этого отдела. Результат работы следующий:
Этот запрос также показывает, как можно использовать SQL/XML для запроса XML и реляционных данных интегрированным способом. Оператор select извлекает данные из обоих типов столбцов (реляционных и XML), а оператор where содержит реляционные и XML предикаты. DB2 9.5 может использовать XML-индексы и реляционные индексы одновременно для определения этих предикатов и максимизации производительности запросов.
В DB2 9.5 можно записать этот запрос еще проще, опустив оператор "passing" в функциях XMLEXISTS и XMLQUERY. Если передавать в XQuery только XML-столбец "deptdoc", можно просто ссылаться в XQuery-запросе на столбец как $DEPTDOC без оператора "passing". Это показано в Query 5.
Query 5
select unitID, XMLQUERY('for $e in $DEPTDOC/dept/employee return $e/name/text()')
from dept d
where unitID LIKE 'WW%' and
XMLEXISTS('$DEPTDOC/dept[@deptID = "V15"]'); |
Этот же запрос можно записать с использованием функции XMLTABLE, как показано в Query 6. В этом формате мы указываем условия для ограничения входных данных и извлечения выходных значений, которые нам нужны. В Query 6 XQuery-запрос в функции XMLTABLE идентифицирует сотрудников, работающих в отделе V15, а path-выражение в операторе COLUMNS ( "name/text()") возвращает их фамилии. Результаты работы аналогичны Query 4 и 5.
Query 6
select d.unitID, T.name
from dept d, XMLTABLE('$d/dept[@deptID="V15"]/employee' passing d.deptdoc as "d"
COLUMNS
name varchar(50) path 'name/text()' ) as T
where unitID LIKE 'WW%'; |
Подход SQL/XML имеет следующие преимущества:
- SQL/XML хорошо подходит при наличии существующего SQL-приложения и необходимости добавления некоторой XML-функциональности то тут, то там, шаг за шагом.
- SQL/XML хорошо подходит для приверженцев SQL и тех, кто хочет сохранить SQL в качестве основного языка, поскольку очень хорошо знаком с ним.
- SQL/XML хорошо подходит тогда, когда запросы должны возвращать данные из реляционных столбцов и XML-столбцов одновременно.
- SQL/XML хорошо подходит тогда, когда запросы должны использовать условия полнотекстового поиска, как показано выше в Query 3.
- SQL/XML хорошо подходит тогда, когда вы хотите возвратить результаты в виде множеств, а отсутствующие XML-элементы представить значениями null.
- SQL/XML хорошо подходит тогда, когда вы хотите использовать маркеры параметров, поскольку DB2 V9 XQuery не поддерживает внешние параметры. Механизмы передачи в XMLQUERY, XMLTABLE и XMLEXISTS позволяют передавать маркеры параметров SQL как переменную ($x) во встроенные XQuery-запросы.
Query 7
select deptdoc
from dept
where XMLEXISTS('$d/dept[@deptID = $x]'
passing deptdoc as "d", cast(? as varchar(8)) as "x"); |
- SQL/XML хорошо подходит для приложений, которые должны интегрировать реляционные и XML-данные. Он предоставляет самое простое средство объединения реляционных и XML-данных. Следующий пример выбирает идентификаторы подразделений всех отделов, имеющих сотрудников, которые определены менеджерами в таблице unit. Этот запрос выполняет соединение между реляционным значением (unit.manager) и XML-значением (//employee/name).
Query 8
select u.unitID
from dept d, unit u
where XMLEXISTS('$d//employee[name = $m]'
passing d.deptdoc as "d", u.manager as "m"); |
Для выполнения этого соединения мы передаем менеджера подразделения в предикат XMLEXISTS, так что реальным условием соединения является XQuery-предикат. Наоборот, мы можем извлечь фамилию сотрудника из XML-документов в SQL-контекст, поскольку условие соединения является SQL-предикатом.
Query 9
select u.unitID
from dept d, unit u
where u.manager = XMLCAST(XMLQUERY('$d//employee/name '
passing d.deptdoc as "d") as char(20)); |
Обычно Query 8 предпочтительнее Query 9, поскольку функция XMLCAST ожидает одно входное значение. Наш пример не работал бы для подразделений с более чем одним сотрудником. Последний вариант с XMLCAST может быть полезен для соединений по XML-значениям, появляющимся только один раз в документе, поскольку это позволяет использовать реляционный индекс по unit.manager. Этот индекс не может использоваться в Query 8, поскольку условие соединения является не реляционным предикатом, а XQuery-предикатом.
- SQL/XML хорошо подходит для группирования и объединения XML. Язык XQuery не предоставляет явной конструкции group-by. Хотя группировки и объединения в XQuery можно выразить при помощи взаимных соединений (self-joins), это не очень удобно. В качестве примера давайте подсчитаем сотрудников, сгруппированных по помещениям, другими словами, количество сотрудников в каждом помещении. Query 10 показывает, как это можно сделать на простом XQuery. Функция db2-fn:xmlcolumn в Query 10 обеспечивает доступ к XML-данным в DB2. Она принимает имя XML-столбца в качестве аргумента и возвращает последовательность XML-значений, хранящихся в этом столбце. Легче использовать SQL/XML-функции, такие как XMLTABLE или XMLQUERY, для извлечения данных из XML-столбцов и последующего использования хорошо известных SQL-концепций группирования и объединения. Query 11 возвращает те же результаты, что и Query 10, но использует XMLTABLE и SQL-оператор group by.
Query 10
XQUERY
for $o in distinct-values(db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept/employee/office)
let $emps := db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept/employee[office/text()=$o]
return
<result><office>{$o}</office><cnt>{count($emps)}</cnt></result>;
Result:
<result><office>216</office><cnt>2</cnt></result>
|
Query 11
select X.office, count(X.emp)
from dept, XMLTABLE ('$d/dept/employee' passing deptdoc as "d"
COLUMNS
emp VARCHAR(30) PATH 'name',
office INTEGER PATH 'office ') as X
GROUP BY X.office;
Result:
216 2
- 1 |
В Query 11 функция XMLTABLE извлекает /dept/employee/name и /dept/employee/office из каждого документа в форме таблицы с двумя столбцами "emp" и "office". Использование SQL group by и агрегатных функций с этой таблицей обычно более эффективно, чем формирование аналогичных результатов на простом XQuery.
Обратите внимание на то, что в Query 10 мы получаем дополнительную строку, поскольку SQL group by формирует также группу для значений NULL, а в нашем примере таблицы имеется один сотрудник без информации о помещении. Query 9 не формирует строки для данного сотрудника, поскольку цикл for выполняет итерации по явным значениям office, не включающим отсутствующую информацию по помещениям.
Давайте сделаем шаг назад и зададим вопрос: что такое XQuery и зачем он нам нужен? Точно так же как SQL является языком запросов, разработанным для реляционной модели, XQuery является языком, разработанным специально для запроса XML-данных. Поскольку XML-данные могут очень отличаться от реляционных данных, нам нужен язык, предназначенный для эффективной работы с XML-данными. Реляционные отношения являются одноуровневыми, очень структурированными, строго типизированными и неупорядоченными, в то время как XML-данные являются упорядоченными, вложенными, иерархическими, не обязательно типизированными и часто нерегулярными и слабоструктурированными. SQL не может работать с ними, но XQuery разработан именно для этого. В частности, XQuery разработан для навигации по дереву XML-документа и для извлечения XML-фрагментов, а также включает в себя выражения для создания, управления, итерации по последовательности XML-элементов и формирования новых XML-данных.
IBM расширила все основные интерфейсы прикладного программирования DB2 (Application Programming Interfaces, API) для поддержки XQuery как первоклассного языка, точно так же как и SQL. К ним относятся CLI/ODBC, встроенный SQL, JDBC и .NET. В результате этого процессор командной строки DB2 тоже поддерживает XQuery. Вы можете передавать XQuery-запросы в существующем виде (as-is), но должны начинать их с ключевого слова XQUERY для информирования DB2 об использовании синтаксического анализатора XQuery, как показано в следующем примере.
Query 13
XQUERY
for $dept in db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept
where $dept/@deptID="PR27"
return $dept/employee/name; |
Query 13 выполняет итерации по каждому элементу "dept" каждого документа department и возвращает фамилии сотрудников, работающих в отделе PR27.
- XQuery хорошо подходит для приложений, работающих только с XML-данными, и которые не нуждаются (или не хотят) в использовании SQL или реляционных структур.
- XQuery хорошо подходит для миграции с базы данных, предназначенной только для XML-данных, на DB2 V9. Имеющиеся XQuery-запросы часто могут работать в DB2 лишь с небольшими изменениями. Например, входные данные для XQuery поступают из функции db2-fn:xmlcolumn() в DB2, тогда как другие базы данных могут называть ее collection(). В этом случае нужно только простое переименование.
- XQuery хорошо подходит тогда, когда нужно встроить результаты запроса в (и возвратить) новые сформированные XML-документы, отличающиеся от документов, которые имеются в базе данных.
- XQuery хорошо подходит для записи соединений между двумя XML-документами, а также для объединения XML-значений. Например, можно записать соединение в Query 12 более понятным и эффективным способом, используя XQuery, как показано в Query 14.
Query 14
XQUERY
for $dept in db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept
for $proj in db2-fn:xmlcolumn("PROJECT.PROJECTDOC")/project
where $dept/@deptID = $proj/deptID
return $dept/employee; |
- С простым XQuery нельзя использовать возможности полнотекстового поиска, предоставляемые DB2 Net Search Extender (NSE). Для этого нужно применять SQL.
- Простой XQuery не позволяет вызывать UDF-функции SQL (определенные пользователем функции) или внешние UDF, написанные на языке C или Java.
- В настоящее время DB2 не предоставляет способа активизировать автономные XQuery-запросы с маркерами параметров. Для передачи параметров в XQuery необходимо использовать SQL/XML для преобразования маркера параметра ("?") в именованные переменные. Например, в Query 13 вы, возможно, захотите использовать символ вопросительный знак (?) в качестве маркера параметра в SQL-стиле вместо литерального значения "PR27". Но это будет некорректный запрос.
В Query 7 вы видели, что SQL/XML позволяет передавать маркер параметров SQL как переменную во встроенное XQuery-выражение. SQL/XML-запросы часто имеют функцию XMLQUERY в операторе select для извлечения частей XML-документов и предикат XMLEXISTS в операторе where для фильтрации указанных документов. Если вы предпочитаете записывать всю логику запроса в одном выражении FLWOR вместо двух отдельных XQuery-вызовов (один в XMLQUERY и один в XMLEXISTS) и все еще используете маркеры параметров, подумайте о том, чтобы переписать Query 13 на Query 15.
Query 15
values( XMLQUERY(
' for $dept in db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept
where $dept/@deptID = $z
return $dept/employee/name'
passing cast(? as varchar(8)) as "z" ) ); |
Query 15 - это SQL/XML-выражение, поскольку представляет собой просто оператор values SQL-запроса fullselect. Оператор values возвращает таблицу значений, указывая выражение для каждого столбца в получаемой таблице. В Query 15 эта таблица имеет одну строку и один столбец типа XML, а функция XMLQUERY формирует значение для выходного столбца. Фамилии всех сотрудников будут возвращаться клиентскому приложению в одном XML-значении. Выражение FLOWR в Query 15 почти аналогично выражению в Query 13, за исключением того, что Query 15 содержит внешнюю переменную ($z), которая передается в функцию XMLQUERY в качестве параметра.
Сам по себе XQuery позволяет обращаться только к XML-данным. Это очень хорошо, если вы имеет дело только с XML-данными, но недостаточно, если ваше приложение нуждается в комбинированном доступе к реляционным и XML-данным, используя всю мощь обоих языков и моделей данных. Это возможно с SQL/XML, рассмотренным выше в данной статье, путем встраивания XQuery в SQL. Обратное встраивание (SQL в XQuery) открывает дополнительные возможности. Кроме списка преимуществ и недостатков, рассмотренных выше, важны следующие аспекты:
- XQuery со встроенным SQL хорошо подходит в случае, когда необходимо обрабатывать только подмножество XML-документов, основанное на условиях по реляционным столбцам. Может быть желательно применить XQuery только к подмножеству XML-документов в XML-столбце. В частности, можно использовать реляционные предикаты для ограничения входных данных для определенного XQuery-запроса. Для данной цели DB2 предоставляет еще одну входную функцию, db2-fn:sqlquery, активизирующую SQL-запрос из XQuery. Эта функция принимает запрос SQL SELECT и возвращает XML-столбец. Например, Query 16 не рассматривает все документы в XML-столбце deptdoc, а имеет встроенный SQL-запрос (где применен предикат where), который предварительно фильтрует XML-документы, присоединяя таблицу unit.
Query 16
XQUERY
for $emp in db2-fn:sqlquery("select deptdoc
from dept d, unit u
where d.unitID=u.unitID and
u.manager = 'Jim Qu'")/dept/employee
where $emp/office = 216
return $emp/name; |
Обычные реляционные индексы по столбцам unitID обеих таблиц, а также по столбцу manager таблицы unit, помогут ускорить встроенный SQL-запрос. DB2 9.5 даже может использовать XML и реляционные индексы одновременно, например, реляционные индексы для встроенного SQL-запроса плюс XML-индекс для XML-предиката $emp/office = 216.
- XQuery со встроенным SQL позволяет использовать полнотекстовый поиск, поскольку можно использовать функцию текстового поиска "contains" в операторе where встроенного SQL-запроса. В Query 17 SQL-запрос внутри XQuery выбирает документы из таблицы dept, в которых встречается слово "sales" где-нибудь в /dept/description. Полнотекстовый индекс быстро находит эти XML-документы, которые затем передаются в выражение FLWOR, извлекающее из этих документов фамилии всех сотрудников. Для нашего примера таблиц Query 18 возвращает аналогичные результаты, но записанные в SQL/XML-нотации.
Query 17
XQUERY
for $emp in db2-fn:sqlquery("
select deptdoc from dept
where CONTAINS(deptdoc, 'SECTION(""/dept/description"") ""sales"" ')=1
")//employee
return $emp/name; |
Query 18
select XMLQUERY('$d//employee/name' passing deptdoc as "d")
from dept
where CONTAINS(deptdoc,'SECTION("/dept/description") "sales" ')=1; |
- XQuery со встроенным SQL может быть полезен для приложений, которым нужно интегрировать реляционные и XML-данные. Реляционные и XML-данные можно запрашивать комбинированным способом. Это также применимо и для SQL/XML. Но вы можете обнаружить, что соединения между XML и реляционными значениями проще в SQL/XML с функцией XMLEXISTS. Сравните Query 19 и Query 20. Query 19 возвращает deptID тех отделов, которые имеют среди своих сотрудников менеджеров подразделений. Встроенный SQL-запрос переводит фамилии менеджеров в таблице unit в XML-тип (в данном случае в XML-строки) и передает их в XQuery. Оператор XQuery where содержит условие соединения, сравнивающее фамилии менеджеров с фамилиями сотрудников. Query 20 - это аналогичное соединение в SQL/XML-нотации.
Query 19
XQUERY
for $m in db2-fn:sqlquery('select XMLCAST(u.manager as XML) from unit u')
for $d in db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept
where $d/employee/name = $m
return $d/data(@deptID); |
Query 20
select XMLQUERY('$d/dept/data(@deptID)' passing d.deptdoc as "d")
from dept d, unit u
where XMLEXISTS('$d/dept/employee[name = $m]'
passing d.deptdoc as "d", u.manager as "m"); |
Отвлекитесь немного и подумайте над тем, как изменить Query 19 и 20, чтобы возвратить соответствующие фамилии сотрудников вместо deptID. Это очень просто в Query 19 - можно просто изменить оператор return на return $m . Очевидная идея для Query 20 - изменить выражение path в функции XMLQUERY на XMLQUERY('$d/dept/employee/name' passing d.deptdoc as "d") . Но при этом возвратились бы фамилии всех сотрудников указанного отдела, а не только менеджеров. Для извлечения только фамилий менеджеров из соответствующего документа department функция XMLQUERY должна включать предикат по name, аналогично XMLEXISTS в Query 20, т.е. XMLQUERY('$d/dept/employee/name[. = $m]' passing d.deptdoc as "d", u.manager as "m") . Отличие состоит в том, что XMLEXISTS применяет предикат для уточнения целых документов, а функция XMLQUERY применяет предикат для поиска конкретной фамилии внутри данного документа, уточненного в XMLEXISTS.
- DB2 9.5 позволяет передавать параметры из XQuery в SQL-запрос, который встроен в функцию db2-fn:sqlquery. Это может быть полезно в разных ситуациях. Одним из примеров является Query 21, который определяет такое же соединение, что и Queries 19 и 20. Оператор "for" выполняет итерацию по фамилиям сотрудников, а оператор "where" проверяет, существует ли менеджер с такой же фамилией в таблице unit. Функция db2-fn:sqlquery принимает $n в качестве дополнительного аргумента. При вычислении запроса select функция "parameter(1)" принимает значение $n. Можно использовать несколько таких параметров. Оператор XQuery "where" принимает значение false, если функция db2-fn:sqlquery возвращает пустую последовательность, и значение true, если она возвращает не пустую последовательность. Причина заключается в существующей семантике XQuery. Следовательно, оператором select мог бы прекрасно быть оператор
select XMLCAST( ''yes'' as XML ) , поскольку здесь важно лишь существование значения, а не само значение.
Query 21
XQUERY
for $n in db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept/employee/name
where db2-fn:sqlquery('select XMLCAST(manager as XML) from unit
where manager = parameter(1)', $n)
return $n/../../data(@deptID); |
Зачем знать о записи соединения подобным способом, если уже имеется вариант, приведенный в Query 19 и 20? В Query 21 предикат join находится на SQL-уровне, что позволяет DB2 использовать реляционный индекс по unit.manager. Query 19 и 20 записывают условие соединения на XML-уровне, поэтому может использоваться XML-индекс по /dept/employee/name . Это аналогично приведенному выше сравнению Query 8 и 9.
- Как видно в Query 19, XQuery со встроенным SQL может быть полезен для передачи реляционных данных в XQuery. Это позволяет комбинировать и объединять реляционные и XML-данные. Query 22 в результате работы формирует документ, содержащий информацию о подразделении и отделе. Информация о подразделении является XML-документом, извлеченным из XML-столбца deptdoc. Информация об отделе поступает из реляционной таблицы unit. Встроенный SQL-запрос использует функции публикации SQL/XML для формирования XML-элемента "Unit" с тремя дочерними элементами, чьи значения берутся из реляционных столбцов таблицы unit, т.е. столбцов unitID, name и manager.
Query 22
XQUERY
let $d := db2-fn:sqlquery("select deptdoc from dept where unitID = 'WWPR' ")
let $u := db2-fn:sqlquery("select XMLELEMENT(NAME ""Unit"",
XMLFOREST(unitID, name, manager))
from unit where unitID = 'WWPR' " )
return <result>
<units>{$u}</units>
<department>{$d}</department>
</result>; |
Результат работы Query 22 будет выглядеть примерно так:
<result>
<units><unit>
<UNITID>WWPR</UNITID>
<NAME>World Wide Markeing</NAME>
<MANAGER>Jim Qu</MANAGER>
</unit>
<unit>
<UNITID>WWPR</UNITID>
<NAME> ... </NAME>
<MANAGER> ... </MANAGER>
</unit>
....
</units>
<department>
<dept deptID="PR27">
<employee id="901">
<name>Jim Qu</name>
<phone>408 555 1212</phone>
</employee>
<employee id="902">
<name>Peter Pan</name>
<office>216</office>
</employee>
</dept>
</department>
</result> |
- XQuery со встроенным SQL является хорошим вариантом, потому что встроенный SQL-запрос может содержать вызовы определенных пользователем функций (User-Defined Function, UDF). UDF-функции широко применяются многими IT-организациями для реализации критичной бизнес-логики и упрощения требований к разработке приложений. Это важно, поскольку простой XQuery не может вызывать UDF-функции.
- Хотя db2-fn:sqlquery позволяет встраивать SQL-запрос в XQuery, в настоящее время отсутствует возможность иметь обычные маркеры параметров SQL-стиля во встроенных SQL-запросах.
- В DB2 9 db2-fn:sqlquery не позволяет передавать значения из XQuery в SQL. Но это ограничение было снято в DB2 9.5, как показано в Query 21.
Одним из моментов, о котором нужно знать, является то, что в зависимости от способа написания конкретного запроса DB2 может представлять результаты запроса в разных форматах. Например, простой XQuery возвращает элементы (например, элементы или фрагменты документов) в наборе данных с одним элементов на строку, даже если несколько элементов поступают из одного и того же документа (строки) базы данных. С другой стороны, SQL/XML может возвращать несколько элементов в одной строке, а не отдельных строках, при использовании функции XMLQUERY. В некоторых случаях это может быть желательным, а в некоторых нет. Все зависит от конкретного приложения. Давайте рассмотрим примеры.
Query 23 и Query 24 запрашивают фамилии сотрудников в документах dept. Query 23 написан на SQL/XML, в то время как Query 24 написан на XQuery.
Query 23
select XMLQUERY('$d/dept/employee/name' passing deptdoc as "d")
from dept; |
Query 24
XQUERY db2-fn:xmlcolumn("DEPT.DEPTDOC")/dept/employee/name; |
Query 23 возвращает одну строку для каждого документа dept, и каждая строка содержит фамилии сотрудников, работающих в этом отделе:
<name>Jim Qu</name> <name>Peter Pan </name> |
<name>Matt Foreman</name> |
Query 24, с другой стороны, возвращает каждую фамилию в отдельной строке:
<name>Jim Qu</name> |
<name>Peter Pan</name> |
<name>Matt Foreman</name> |
Результат Query 24 обычно больше подходит для использовании в приложении, т.к. предоставляет по одному XML-значению. Однако в этом случае неизвестно, какие фамилии поступили из одного и того же документа department. Результат работы Query 23 содержит эту информацию, но может меньше подходить для приложения, поскольку может потребоваться разбиение строк на отдельные фамилии. Если приложение использует XML-анализатор для приема каждой строки XML-результата из DB2, первая строка из Query 21 будет отброшена синтаксическим анализатором, потому что она не является грамматически правильным документом (отсутствует один корневой элемент). Решить проблему корневого элемента можно путем добавления в Query 23 конструктора XMLELEMENT, как показано в Query 25.
Query 25
Select XMLELEMENT(name "employees",
XMLQUERY('$d/dept/employee/name' passing d.deptdoc as "d"))
from dept d; |
Это меняет результат запроса таким образом, что каждая строка будет являться грамматически правильным XML-документом:
<employees><name>Jim Qu </name><name>Peter Pan </name></employees> |
<employees><name>Matt Foreman</name></employees> |
.... |
Вспомните, что Query 15 использует оператор SQL values и функцию XMLQUERY для разрешения передачи параметра в XQuery. Но результатом работы Query 15 является одна строка, содержащая фамилии всех сотрудников. Если предпочтительнее получить фамилию каждого сотрудника в отдельной строке и есть также необходимость в использовании маркеров параметров, можно применить функцию XMLTABLE в Query 26.
Query 26
select X.*
from dept d, XMLTABLE('for $dept in $d/dept
where $dept/@deptID = $z
return $dept/employee/name'
passing d.deptdoc as "d", cast(? as varchar(10)) as "z"
COLUMNS
"name" XML PATH '.') as X ; |
DB2 pureXML предлагает богатый набор вариантов запроса XML-данных. Вы будете выбирать вариант, подходящий для вас, основываясь на требованиях приложения и его характеристиках. Если нужно комбинировать реляционные и XML-данные, то лучшим вариантом в большинстве ситуаций будет являться SQL/XML. В частности, SQL/XML - это вариант, который позволяет применять маркеры параметров с XML-данными. Если имеются приложения, работающие только с XML-данными, хорошим выбором будет автономный XQuery, который может быть расширен встроенным SQL для разрешения полнотекстового поиска и активизации UDF-функций. Рассмотренные в данной статье примеры помогут вам принять обоснованное решение. Вы можете использовать шаблоны запросов из данной статьи в качестве отправной точки для написания собственных запросов к XML-данным.
Общей рекомендацией является реализация такого уровня сложности в ваших запросах, какой действительно необходим. Например, определенно возможно иметь XQuery со встроенным SQL-запросом, имеющим встроенный XQuery и т.д. Но наш опыт показывает, что для написания желаемой логики запроса вложения нескольких языков более чем на один уровень обычно не требуется. Поэтому мы рекомендуем использовать только один уровень встраивания XQuery в SQL или наоборот.
|