Определение предикатов в шаге выражения пути
Как описано в разделе Выражения пути в 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, применяются следующие правила:
Если значение выражения предиката представляет пустую последовательность, значение предиката равно 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>, то данное расположение цеха не включается в результат.
В качестве значений предикатов можно использовать только 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> корневого узла.
Если значение выражения предиката представляет собой простое значение типа 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"/>
Если значение выражения предиката представляет последовательность как минимум из одного узла, значение истинности предиката равно 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() уменьшают количество элементов шага фильтрации выражения до одного.