Поделиться через


Определение предикатов в шаге выражения пути

Как описано в разделе Выражения пути в XQuery, шаг оси в выражении пути содержит следующие компоненты:

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

Предикаты

Предикат используется для фильтрации последовательности узлов при помощи заданного теста. Выражение предиката заключено в квадратные скобки и привязано к последнему узлу выражения пути.

Предположим, что значение SQL-параметра (x) типа xml объявлено следующим образом:

declare @x xml
set @x = '
<People>
  <Person>
    <Name>John</Name>
    <Age>24</Age>
  </Person>
  <Person>
    <Name>Goofy</Name>
    <Age>54</Age>
  </Person>
  <Person>
    <Name>Daffy</Name>
    <Age>30</Age>
  </Person>
</People>
'

В этом случае следующие выражения, использующие значение предиката, равное [1], на каждом из трех уровней узлов, будут допустимыми:

select @x.query('/People/Person/Name[1]')
select @x.query('/People/Person[1]/Name')
select @x.query('/People[1]/Person/Name')

Обратите внимание, что в каждом случае предикат привязан к узлу выражения пути, где он применяется. Например, первое выражение пути выбирает первый элемент <Name> в каждом узле /People/Person и при помощи заданного экземпляра XML возвращает следующий результат:

<Name>John</Name><Name>Goofy</Name><Name>Daffy</Name>

Однако второе выражение пути выбирает все элементы <Name> первого узла /People/Person. Таким образом, оно возвращает следующий результат:

<Name>John</Name>

Порядок вычисления предикатов можно менять при помощи круглых скобок. Например, в следующем выражении круглые скобки используются для разделения пути (/People/Person/Name) от предиката [1]:

select @x.query('(/People/Person/Name)[1]')

В этом примере порядок применения предикатов изменяется. Сначала вычисляется путь, заключенный в круглые скобки (/People/Person/Name), а затем оператор предиката [1] применяется к набору, содержащему все узлы, которые соответствуют этому пути. Если убрать круглые скобки, порядок операций будет другим; предикат [1] будет применяться в качестве теста узла child::Name, так же, как и в первом примере.

Квалификаторы и предикаты

Квалификаторы можно использовать более одного раза в фигурных скобках предиката. Например, в предыдущем примере можно использовать более одного квалификатора в сложном подвыражении предиката следующим образом.

select @x.query('/People/Person[contains(Name[1], "J") and xs:integer(Age[1]) < 40]/Name/text()')

Результат выражения предиката преобразуется в значение типа Boolean и называется истинностью предиката. Набор результатов содержит только те узлы последовательности, для которых значение предиката равно True. Остальные узлы отбрасываются.

Например, предикат содержит второй шаг следующего выражения пути:

/child::root/child::Location[attribute::LocationID=10]

Условие, заданное при помощи предиката, применяется ко всем дочерним элементам узла <Location>. Возвращаются только те результаты, у которых значение атрибута LocationID равно 10.

Предыдущее выражение пути выполнялось в следующей инструкции SELECT:

SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
 /child::AWMI:root/child::AWMI:Location[attribute::LocationID=10]
')
FROM Production.ProductModel
WHERE ProductModelID=7

Проверка истинности предикатов

Чтобы определить истинность значения предиката согласно спецификации XQuery, применяются следующие правила:

  1. Если значение выражения предиката представляет пустую последовательность, значение предиката равно False.

    Например:

    SELECT Instructions.query('
    declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
     /child::AWMI:root/child::AWMI:Location[attribute::LotSize]
    ')
    FROM Production.ProductModel
    WHERE ProductModelID=7
    

    Выражение пути в этом запросе возвращает только такие узлы элемента <Location>, у которых задан атрибут LotSize. Если предикат возвращает пустую последовательность для некоторого элемента <Location>, то данное расположение цеха не включается в результат.

  2. В качестве значений предикатов можно использовать только xs:integer, xs:Boolean или узел*. Если задан узел*, предикат принимает значение True, если такие узлы существуют, и False в противном случае. При использовании других числовых типов, например double или float, возникает ошибка статической типизации. Значение предиката выражения равно True тогда и только тогда, когда полученное значение типа integer равно значению позиции контекста. Кроме того, только целые литеральные значения и функция last() уменьшают количество элементов шага фильтрации выражения до одного.

    Например, следующий запрос возвращает третий дочерний узел элемента <Features>.

    SELECT CatalogDescription.query('
    declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
    declare namespace wm="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain";
     /child::PD:ProductDescription/child::PD:Features/child::*[3]
    ')
    FROM Production.ProductModel
    WHERE ProductModelID=19
    

    При изучении предыдущего запроса обратите внимание на следующее:

    • Третий шаг в выражении задает выражение предиката со значением 3. Таким образом, значение истинности предиката для этого выражения — True только для тех узлов, позиция которых в контексте равна 3.

    • Третий шаг также содержит символ-шаблон (*), который указывает на все узлы в тесте узлов. Тем не менее предикат фильтрует узлы и возвращает только те из них, которые находятся на третьей позиции.

    • Запрос возвращает третий дочерний узел элемента <Features> дочерних узлов элемента <ProductDescription> корневого узла.

  3. Если значение выражения предиката представляет собой простое значение типа Boolean, значение истинности предиката совпадает с ним.

    Например, следующий запрос выполняется над переменной типа xml, которая содержит экземпляр XML со списком клиентов. Запрос получает только тех клиентов, у которых есть дети. Результатом запроса будет <HasChildren>1</HasChildren>.

    declare @x xml
    set @x='
    <Survey>
      <Customer CustomerID="1" >
      <Age>27</Age>
      <Income>20000</Income>
      <HasChildren>1</HasChildren>
      </Customer>
      <Customer CustomerID="2" >
      <Age>27</Age>
      <Income>20000</Income>
      <HasChildren>0</HasChildren>
      </Customer>
    </Survey>
    '
    declare @y xml
    set @y = @x.query('
      for $c in /child::Survey/child::Customer[( child::HasChildren[1] cast as xs:boolean ? )]
      return 
          <CustomerWithChildren>
              { $c/attribute::CustomerID }
          </CustomerWithChildren>
    ')
    select @y
    

    При изучении предыдущего запроса обратите внимание на следующее:

    • Выражение в цикле for имеет два шага, предикат задан во втором шаге. Значение предиката имеет тип Boolean. Если это значение равно True, значение истинности предиката также равно True.

    • Запрос возвращает дочерние узлы элемента <Customer>, для которых значение предиката равно True, дочерних узлов элемента <Survey> корневого узла. Результат:

      <CustomerWithChildren CustomerID="1"/> 
      
  4. Если значение выражения предиката представляет последовательность как минимум из одного узла, значение истинности предиката равно True.

Например, следующий запрос получает идентификатор ProductModelID моделей продуктов, у которых описание в каталоге XML содержит по крайней мере один компонент — дочерний узел элемента <Features> из пространства имен, связанного с префиксом wm.

SELECT ProductModelID
FROM   Production.ProductModel
WHERE CatalogDescription.exist('
             declare namespace PD="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription";
             declare namespace wm="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelWarrAndMain";
             /child::PD:ProductDescription/child::PD:Features[wm:*]
             ') = 1

При изучении предыдущего запроса обратите внимание на следующее:

  • В предложении WHERE указана синтаксическая конструкция метод exist() (тип данных xml).

  • Выражение пути в методе exist() указывает предикат во втором шаге. Если значение предиката возвращает последовательность из хотя бы одного компонента, то значение истинности для этого выражения предиката — True. В этом случае, поскольку метод exist() возвращает значение True, возвращается ProductModelID.

Статическая типизация и фильтры предикатов

Предикаты также могут влиять на статически выведенный тип выражения. Целые литеральные значения и функция last() уменьшают количество элементов шага фильтрации выражения до одного.