Выражения пути — указание предикатов

Применимо к:SQL Server

Как описано в разделе, выражения пути в 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 и возвращает следующее:

<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 или node*. Для узла*предикат оценивается как True, если есть какие-либо узлы и False для пустой последовательности. При использовании других числовых типов, например double или float, возникает ошибка статической типизации. Значение предиката выражения равно True тогда и только тогда, когда полученное значение типа integer равно значению позиции контекста. Кроме того, только целые литеральные значения и функция last() снижают кратность отфильтрованного выражения шага до 1.

    Например, следующий запрос извлекает третий дочерний узел <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, экземпляр 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  
    

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

    • Выражение в цикле имеет два шага, а второй шаг задает предикат. Значение предиката имеет тип 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() снижают кратность отфильтрованного выражения шага до одного.