Главная » SQL, Базы данных » Язык XQuery

0

Одним из недостатков языка XPath является то, что он в своей основе представляет собой просто механизм адресации; применяемые в нем обозначения пути позволяют переходить по существующим узлам в иерархии, но не дают возможности формировать узлы, которые еще не существуют. Иными словами,  язык XPath немного напоминает "реляционный" язык (здесь слово  "реляционный"  заключено в кавычки, поскольку настоящие реляционные языки, безусловно, не являются навигационными) в том смысле, что он поддерживает  операции сокращения и проекции, но не операции соединения19. Именно эта причина отчасти послужила стимулом к созданию языка XQuery; по сравнению с языком Xpath одним из основных дополнений, предусмотренных в языке XQuery, как раз и является способность формировать новые узлы.

Такая возможность иллюстрируется в приведенном ниже первом примере. И в данном случае  предполагается,  что  существует  документ  XML,  называемый  PartsRelation, точно такой же, как и в предыдущем подразделе. Предположим  также, что имеются структурированные аналогичным  образом документы  SuppliersRelation и ShipmentsRelation. Таким образом, ниже приведена  формулировка на языке XQuery сле дующего запроса: "Для каждой поставки определить имя поставщика, имя детали и объ ем поставки".

<Result>

{ for $spx in document("ShipmentsRelation.xml")

//ShipmentTuple,                                                                                                                                                                     .

$sx in document("SuppliersRelation.xml")

//SupplierTuple[SNUM = $spx/SNUM] ,

$px in document("PartsRelation.xml")

//PartTuple[PNUM = $spx/PNUM] order by SNAME,

PNAME return

<ResultTuple>

{ $SX/SNAME, $px/PNAME, $spx/QTY }

</ResultTuple> }

</Result>

Пояснение

1.  В целом это выражение вычисляет (формирует) единственный элемент Result, содержащий последовательность элементов ResultTuple. В  качестве  дополнительного замечания отметим, что если бы были удалены охватывающие это выражение

19   Это  утверждение  в  определенной  степени  является  слишком  упрощенным;  фактически   язык   XPath поддерживает своего рода операцию декартова произведения (которая, безусловно, представляет собой вырожденный случай операции соединения). Но этот язык не поддерживает операции соединения в какой-либо более общей форме.

дескрипторы Result, то оставшееся выражение по-прежнему представляло бы собой допустимый запрос XQuery, но возвращало результат, не являющийся формально правильным.

2.    Удобный способ пояснения семантики всего этого выражения (за исключением охватывающих дескрипторов Result) состоит в использовании следующего его аналога — выражения реляционного исчисления.

{ SX.SNAME, PX.PNAME, SPX.QTY } WHERE SX.SNUM = SPX.SNUM AND PX.PNUM = SPX.PNUM

Это выражение фактически требует выполнения указанного соединения  отношений поставщиков, деталей и поставок, а затем получения  проекции результата этого соединения по атрибутам SNAME, PNAME И QTY.

Примечание. Здесь не показан аналог шага "order by" в запросе XQuery,  поскольку операция  упорядочения  "order  by"  не  является  реляционной.  Кроме                                                                                  того, применяются обычно принятые в этой книге соглашения, касающиеся имен переменных области  значений  (SX,  PX  и  SPX  —  переменные  области  значений,  пробегающие, соответственно,   по   отношениям    поставщиков,    деталей   и   поставок).   Наконец, игнорируется   тот   факт,   что    в   реляционных   выражениях   дубликаты   кортежей устраняются автоматически, а в выражениях XQuery — нет.

3.    На основании изложенного в предыдущем абзаце можно сделать вывод, что пере менные $sx, $px и $spx языка XQuery действуют в определенной степени ана логично переменным области значений в реляционном исчислении. Но такая аналогия является не совсем точной и способна сбить с толку; формулировка запроса XQuery в действительности не так уж  похожа на реляционную, поскольку вполне очевидно, что она по своему характеру является полностью процедурной (в этой связи рекомендуем  ознакомиться с аннотацией к [27.3]). На самом деле формулировка запроса  XQuery весьма напоминает приведенную ниже формулировку запроса в виде вложенных циклов (здесь она представлена на псевдокоде, а в качестве разделителя используется ".", а не "/").

do for each shipment $spx ;

do for each supplier $sx where Ssx.snum = $spx.snum

;                                        do for each part $px where $px.pnum =

$spx.pnum ;                                           emit { $sx. SNAME, $px. PNAME,

$spx.QTY } ;

end do ;                                          end do ;

Из этого следует, что фактически переменные $sx, $px и $spx гораздо  больше напоминают переменные управления циклом (определяемые в том же смысле, как и в обычном языке программирования), чем переменные области значений. Более того, следует отметить, что итерацию по кортежам отношения поставок пришлось выполнять именно во внешнем цикле; это означает, что вначале пришлось ввести в действие переменную управления циклом $spx, поскольку две остальные переменные определены в  терминах  этой переменной. Такой факт может повлечь за собой некоторые интересные следствия с точки зрения организации работы оптимизатора. В отличие от нее, две другие переменные, $sx и $рх, могут быть введены в любом порядке. Тем не менее, вопрос о том, оказывает ли порядок введения

этих переменных какое-то влияние на работу оптимизатора, также может оказаться весьма интересным. По крайней мере, следует отметить, что порядок, в котором вводятся эти переменные, оказывает влияние на то, в каком порядке вырабатываются результирующие элементы (см. описание конструкции return в п. 7); таким образом, вообще говоря, аналоги  выражений A JOIN Bи B   JOIN А на языке XQuery не являются эквивалентными.

На основании приведенных выше соображений можно смело  утверждать, что XQuery фактически в большей степени напоминает язык программирования, чем язык запросов, предназначенный для конечного пользователя.

4.    Теперь рассмотрим, в частности, конструкцию for, а именно ту часть конструк ции, которая предшествует первой запятой. Приведенное ниже выражение воз вращает абстрактную ("синтаксически проанализированную") версию документа XML, который содержится в файле ShipmentsRelation.xml, с использованием в качестве контекстного всего узла документа.

document("ShipmentsRelation.xml")

Спецификация "//ShipmentTuple" свидетельствует о том, что нас  интересует элементы ShipmentTuple из данного документа, а  спецификация "for $spx in" указывает, что переменная $spx должна принять в качестве своего значения каждый из этих кортежей поставок по очереди, в том порядке, в котором они при сутствуют в данном документе.

5.    Следующая часть конструкции for является аналогичной, за исключением того, что переменная $sx принимает свои значения только среди тех поставщиков, для которых значение SNUM равно текущему значению $ spx.

$sx in document ("SuppliersRelation.xml")

//SupplierTuple[SNUM = $spx/SNUM]

6.    Последняя часть конструкции for аналогична описанной выше части. "

7.    Конструкция return выполняется для каждой комбинации значений переменных

$sx, $px и $spx, в том порядке, в котором эти значения вырабатываются конструкцией for. Поэтому в данном примере конструкция return вырабатывает элементы ResultTuple в последовательности, которая определяется правилом, что значения $рх изменяются чаще всего, значения $sx изменяются менее часто, а значения $spx изменяются наименее часто.

8.    Конструкция order by в основном не требует пояснений. Но заслуживает вни мания то, что она появляется перед соответствующей конструкцией return. Такое расположение указанных конструкций позволяет упорядочивать результаты на основании значений, которые еще фактически не появляются в результатах (как, например, в запросе SELECT CITY FROM P ORDER BY WEIGHT на языке SQL). Но концептуально и в этом случае конструкция return должна быть вы полнена в первую очередь, поскольку упорядочение невозможно выполнить до тех пор, пока не появится нечто, предназначенное для упорядочения.

Ниже приведен второй пример: "Определить номера деталей и общий объем поставки для деталей, поставляемых двумя или несколькими поставщиками".

for $pnum in distinctvalues(document("ShipmentsRelation.xml")//PNUM) let $spx

:= document("ShipmentsRelation.xml")

//ShipmentTuple[PNUM =

$prium] where count ( $spx ) > 1 order by PNUM return

<Result>

{  $pnum,

<totqty> { sum ( $spx/gty ) } </totqty> }

</Result>

Пояснение

1.  В данном примере показано полное выражение FLWOR (FLWOR — сокращение от for   +   let   +   where   +   order   by   +   return; оно произносится как слово "flower" — флауэ).

2.  Обратите внимание на использование ключевого слова "distinct-values" в конструкции for для устранения дубликатов номеров деталей; переменная $pnum принимает значения только среди разных номеров деталей в поставках.

3.  Конструкция let отличается от конструкции for тем, что заданная в ней переменная не используется для итераций по указанной последовательности значений; вместо этого такой переменной полностью присваивается соответствующая последовательность значений. Кроме того, в данном примере конструкция let вычисляется для каждого отдельного номера детали в поставке по очереди, поскольку она фактически вложена в конструкцию for.

4.  Затем конструкция where вызывает вычисление оставшейся части этого выраже ния тогда и только тогда, когда текущая последовательность поставок (т.е. после довательность поставок с текущим номером детали) имеет длину больше единицы. поддерживает обычные агрегирующие операторы count, sum, avg, max и min.

5.  Следует опять отметить, что все это выражение в целом весьма напоминает проце дурное. Аналог этого выражения на псевдокоде приведен ниже.

do   for   each  distinct    shipment part   number   $pnum    ;

do for all    shipments   $spx  where   $spx.pnum  =    $pnum    ; if    (    count  of   such  shipments   $spx    )    >   1   then

emit  {   $pnum,  sum  (   $spx.qty  )    }   ;

end  if   ; end do    ;

end do    ;

Реляционный аналог данного выражения выглядит следующим образом.

{ SPX.PNUM, SUM ( SPY WHERE SPY.PNUM = SPX.PNUM, QTY ) } WHERE COUNT ( SPY WHERE SPY.PNUM = SPX.PNUM ) > 1

Теперь следует отметить, что приведенные выше примеры, возможно,  являются не совсем реальными. Это связано с именно с тем, что они по своему характеру в определенной степени напоминают реляционные; в частности, в этих примерах почти не учитывается специфика документов XML, которые обычно имеют иерархическую структуру.

Поэтому рассмотрим следующий вариант применяемого проекта. Вначале предположим, что документ PartsRelation является точно таким же, как и прежде. Но допустим, что вместо документов SuppliersRelation И  ShipmentsRelation теперь применяется документ SuppliersOverShipments,  который характеризуется описанными ниже особенностями.

■     Корневой элемент содержит последовательность элементов Supplier.

■     Каждый элемент Supplier включает элементы SNUM, SNAME, STATUS и CITY, за которыми расположена последовательность элементов shipment.

■     Каждый элемент Shipment содержит элемент PNUM и элемент QTY.

Структура этого документа показана на рис. 27.4.

Теперь рассмотрим запросы: "Определить поставщиков, которые поставляют деталь Р2" и "Определить детали, поставляемые поставщиком S2". Ниже приведены реляционные формулировки этих запросов.

SX WHERE EXISTS SPX ( SPX.SNUM = SX. SNUM AND SPX.PNUM = ‘ P2 ‘ )

PX WHERE EXISTS SPX ( SPX.PNUM = PX.PNUM AND SPX.SNUM = ‘S2′ )

Примечание. Для упрощения предполагается, что значения SNUM и PNUM представляют собой простые символьные строки, а не значения какого-то определяемого пользователем типа. Итак, ниже приведена формулировка первого запроса на языкеХО_иегу.

for $sx in document("SuppliersOverShipments.xml")//Supplier where $sx//PNUM = "P2"

return

<Result>

{ $SX//SNUM, $sx//SNAME, $sx//STATUS, $Sx//CITY }

</Result>

А формулировка второго запроса на языке XQuery выглядит следующим образом.

let $sx := document("SuppliersOverShipments.xml")

//Supplier[SNUM = "S2"] return <Result>

{ document("PartsRelation.xml")

//PartTuple[PNUM = $sx//PNUM]

} </Result>

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

В завершении данного раздела приведем еще несколько замечаний.

■     Как и в первоначально сформулированной версии языка SQL [4.9]—[4.11], в языке XQuery отсутствует явная поддержка операции соединения. И действительно, в [27.29] конкретно указано, что "для вычисления  соединений могут применяться [выражения FLWOR]"; под этим в сущности подразумевается, что при вычислении каждого соединения пользователь должен указывать необходимую для этого последовательность шагов. Но язык XQuery включает явную поддержку операций объединения, пересечения и разности (конструкция except) с устранением дубликатов.

■   включает также явную поддержку кванторов существования и всеобщности. Ниже приведены соответствующие примеры.

some $x in ( 2, 4, 8 ) satisfies $x <

7 every $x in ( 2, 4, 8 ) satisfies $x

< 7

Первое из этих выражений принимает истинное значение, а второе— ложное.

■     Как описано в главах 7 и 8, с реляционной моделью связано (очень важное) поня тие полноты. По-видимому, аналогичного понятия не существует ни в языке XQuery, ни в языке XML в целом20.

Источник: Дейт К. Дж., Введение в системы баз данных, 8-е издание.: Пер. с англ. — М.: Издательский дом «Вильямс», 2005. — 1328 с.: ил. — Парал. тит. англ.

По теме:

  • Комментарии