pureXML в DB2 9: Каким способом запрашивать XML-данные?

В DB2 9 была добавлена поддержка pureXML®. Это означает, что XML-данные записываются и запрашиваются в своем собственном иерархическом формате. Для запроса XML-данных DB2 предлагает два языка: SQL/XML и XQuery. Можно использовать XQuery и SQL поодиночке, но можно также встраивать XQuery в SQL и наоборот. Это дает большую гибкость и несколько вариантов для запроса XML-данных. Каждый данный вариант полезен при определенных обстоятельствах. В статье мы рассматриваем эти варианты, свойственные им преимущества и недостатки, а также даем рекомендации по выбору подходящего под ваши требования варианта. Статья была обновлена для версии DB2 9.5.

Маттиас Никола, технический специалист, IBM

Доктор Никола (Nicola) является ведущим техническим специалистом по производительности DB2/XML в Лаборатории IBM в Силиконовой Долине (Silicon Valley Lab). Он работает над всеми аспектами производительности XML в DB2, включая XQuery, SQL/XML и все возможности, присущие XML в DB2. Доктор Никола тесно сотрудничает с коллективами разработчиков DB2 XML, также как и с заказчиками и деловыми партнерами, использующими XML, помогая им в проектировании, реализации и оптимизации решений на XML. До начала работы в IBM Доктор Никола работал над производительностью информационных хранилищ в Informix Software. Он также участвовал в течение четырех лет в исследовательских и промышленных проектах по распределенным и реплицированным базам данных. Он получил докторскую степень по вычислительной технике в 1999 в Политехническом Университете Аахена, Германия.



02.04.2008

Введение

Встроенная поддержка 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 уже публиковалось на developerWorks в статьях: "Запрос 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. Резюме
Простой SQLSQL/XMLПростой XQueryXQuery со встроенным SQL/XML
XML-предикаты-++++++
Реляционные предикаты++++-+
XML и реляционные предикаты-++-++
Объединение XML с реляционными данными-++-++
Объединение XML с XML-+++++
Преобразование XML-данных-o++++
Вставка, обновление, удаление++++--
Маркеры параметров+++--
Полнотекстовый поиск+++-++
Объединение и группировка-++oo
Вызовы функций++++-++

В приведенной выше таблице "-" обозначает, что данный язык не поддерживает эту функциональность, "+" означает, что функциональность поддерживается, но может существовать более эффективный или удобный способ, "++" обозначает, что данный язык очень хорошо подходит для выражения указанной функциональности, и, наконец, "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)
unitIDdeptdoc
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),...)
unitIDnamemanager...
WWPRWorldwide MarketingJim Qu...
S-USESales US East CoastTom Jones...
............


Простой SQL

Простой 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 (XQuery/XPath, встроенный в SQL)

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 для возврата фамилий всех сотрудников из документа для этого отдела. Результат работы следующий:

WWPRMatt Foreman

Этот запрос также показывает, как можно использовать 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/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, не включающим отсутствующую информацию по помещениям.

Недостатки SQL/XML

  • SQL/XML не всегда является наилучшим вариантом для преобразования одного XML-документа в другой XML-документ. Более подходящим может быть использование автономного XQuery (и, зачастую, более интуитивным при работе только с XML-данными). Но DB2 9.5 предлагает также функцию XSLTRANSFORM, которую можно использовать в SQL-запросах для преобразования XML-документов с таблицами стилей XSLT. В частности, функция XMLQUERY может быть аргументом функции XSLTRANSFORM.
  • Вы можете обнаружить, что выражение соединений между двумя XML-столбцами (или в более широком смысле - между двумя XML-значениями) может быть более наглядным на простом XQuery, чем на SQL/XML. Например, Query 12 соединяет XML-столбцы таблиц dept и project, возвращая фамилии сотрудников, работающих над любым из проектов.
    Query 12
    select XMLQUERY('$d/dept/employee' passing d.deptdoc as "d")
    from dept d, project p
    where XMLEXISTS('$d/dept[@deptID=$p/project/deptID]'
                     passing d.deptdoc as "d", p.projectDoc as "p");

    В Query 12 мы передаем каждый документ dept и project в XQuery внутри XMLEXISTS и записываем здесь условие соединения. Вам может показаться, что проще написать такое соединение на простом XQuery (Query 14).

XQuery как автономный язык

Давайте сделаем шаг назад и зададим вопрос: что такое 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

  • 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

  • С простым 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 со встроенным SQL

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

Преимущества XQuery со встроенным SQL

  • 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-функции.

Недостатки XQuery со встроенным SQL

  • Хотя db2-fn:sqlquery позволяет встраивать SQL-запрос в XQuery, в настоящее время отсутствует возможность иметь обычные маркеры параметров SQL-стиля во встроенных SQL-запросах.
  • В DB2 9 db2-fn:sqlquery не позволяет передавать значения из XQuery в SQL. Но это ограничение было снято в DB2 9.5, как показано в Query 21.

Результаты XML-запроса

Одним из моментов, о котором нужно знать, является то, что в зависимости от способа написания конкретного запроса 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 или наоборот.

Благодарности

Благодарю Дона Чэмберлина (Don Chamberlin), Берта Ван дер Линдена (Bert van der Linden), Синди Саракко (Cindy Saracco), Жан-Эйка Мишелса (Jan-Eike Michels) и Хардипа Сингха (Hardeep Singh) за рецензии на данную статью.


Загрузка

ОписаниеИмяРазмер
Сценарий для запросов из данной статьиDDLandQueries.txt9KB

Ресурсы

Научиться

Получить продукты и технологии

  • Загрузите пробную версию DB2 9 для тестирования запросов, описанных в данной статье.
  • Теперь вы можете использовать DB2 бесплатно. Загрузите DB2 Express-C, бесплатную версию DB2 Express Edition для сообщества, предлагающую такие же базовые функции, что и DB2 Express Edtion, и обеспечивающую прочный фундамент для создания и развертывания приложений.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Information Management, XML
ArticleID=299182
ArticleTitle=pureXML в DB2 9: Каким способом запрашивать XML-данные?
publish-date=04022008