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


Типы данных XPath (SQLXML 4.0)

Набор типов данных в Microsoft SQL Server, XPath и XML Schema (XSD) сильно отличается. Например, в XPath отсутствуют целочисленные типы данных и тип данных для обозначения даты, а в SQL Server и XSD таких типов множество. Типы данных XSD определяют время с точностью до наносекунды, а SQL Server только до одной трехсотой доли секунды. Поэтому не всегда возможно сопоставить один тип другому. Дополнительные сведения о сопоставлении типов данных SQL Server и XSD см. в разделе Приведение типов данных и заметка sql:datatype (SQLXML 4.0).

В XPath есть три типа данных. string, number и boolean. Тип данных number — это всегда тип двойной точности с плавающей запятой, соответствующий стандарту IEEE 754. Тип данных SQL Server float(53) ближе всего к типу number в XPath. Однако float(53) не совсем соответствует IEEE 754. В частности, этот тип не содержит ни значения NaN (не число), ни значения бесконечности. Попытка преобразовать нечисловую строку в тип number и попытка деления на ноль вызывают ошибки.

Преобразования в XPath

При использовании запроса XPath, например, OrderDetail[@UnitPrice > "10.0"], явные и неявные преобразования типов данных могут различными неочевидными способами изменить значение запроса. Поэтому важно знать, как реализована система типов данных в XPath. Спецификация языка XPath — рекомендации консорциума W3C XML Path Language (XPath) версии 1.0 от 8 октября 1999 г.; с этим документом можно ознакомиться на веб-сайте W3C по адресу: http://www.w3.org/TR/1999/PR-xpath-19991008.html.

Операторы XPath делятся на четыре категории:

  • Логические операторы (и, или)

  • Реляционные операторы (<, >, <=, >=)

  • Операторы равенства (=, !=)

  • Арифметические операторы (+, -, *, div, mod)

Операторы разных категорий по-разному преобразуют операнды. При необходимости операторы XPath неявно преобразуют операнды. Арифметические операторы преобразуют операнды к типу number, и результатом их выполнения является число. Логические операторы преобразуют операнды к типу boolean, и результатом их выполнения является логическое значение. Результатом выполнения реляционных операторов и операторов равенства является логическое значение. Однако правила преобразования, которыми пользуются операторы, зависят от первоначальных типов операндов, как показано в таблице.

Операнд

Реляционный оператор

Оператор равенства

Оба операнда представляют собой наборы узлов.

Значение TRUE, если и только если в первом наборе узлов есть такой узел и во втором наборе узлов есть такой узел, что сравнение их значений string вернет значение TRUE.

То же.

Один — набор узлов, другой — string.

Значение TRUE, если и только если в наборе узлов есть такой узел, который после преобразования в number при сравнении с объектом типа string, преобразованным в тип number, вернет значение TRUE.

Значение TRUE, если и только в том случае, когда в наборе узлов есть такой узел, который после преобразования в string при сравнении с string вернет значение TRUE.

Один — набор узлов, другой — number.

Значение TRUE, если и только в том случае, когда в наборе узлов есть такой узел, который после преобразования в number при сравнении с number вернет значение TRUE.

То же.

Один — набор узлов, другой — boolean.

Значение TRUE, если и только если в наборе узлов есть такой узел, который после преобразования в boolean, а затем в number при сравнении с объектом типа boolean, преобразованным в тип number, вернет значение TRUE.

Значение TRUE, если и только в том случае, когда в наборе узлов есть такой узел, который после преобразования в boolean при сравнении с boolean вернет значение TRUE.

Ни один из них не представляет собой набор узлов.

Преобразует оба операнда к типу number, а затем сравнивает их.

Преобразует оба операнда к одному типу, а затем сравнивает их. Преобразует к типу boolean, если хотя бы один из операндов принадлежит к типу boolean, и к типу number, если хотя бы один из операндов принадлежит к типу number; в противном случае преобразует к типу string.

ПримечаниеПримечание

Поскольку реляционные операторы XPath всегда преобразуют операнды к типу number, сравнение типов string невозможно. Чтобы включить сравнение дат, SQL Server 2000 предлагает следующее изменение спецификации XPath. Когда реляционный оператор сравнивает тип string с типом string, набор узлов с типом string или набор узлов со строковыми значениями с другим таким же набором, проводится сравнение по типу string, а не по типу number.

Преобразования наборов узлов

Преобразования наборов узлов не всегда интуитивно понятны. Чтобы преобразовать набор узлов в тип string, берется строковое значение только первого узла набора. Чтобы преобразовать набор узлов к типу number, он сначала преобразуется в тип string, а затем значение типа string преобразуется в тип number. Чтобы преобразовать набор узлов к типу boolean, производится его проверка на существование.

ПримечаниеПримечание

SQL Server не проводит выборки из наборов узлов по положению в наборе. Например, запрос XPath Customer[3] означает третьего заказчика; в SQL Server такой тип выборки не поддерживается. Поэтому преобразования набора узлов в тип string или в тип number, согласно спецификации XPath, не реализованы. SQL Server использует семантику «любой» там, где спецификация XPath использует семантику «первый». Например, согласно спецификации W3C по XPath, запрос XPath Order[OrderDetail/@UnitPrice > 10.0] выберет заказы, у которых в первой строке OrderDetail значится цена UnitPrice, превышающая 10.0. В SQL Server этот запрос XPath выберет заказы, у которых в любой строке OrderDetail значится цена UnitPrice, превышающая 10.0.

Преобразование к типу boolean проводит проверку на существование, поэтому запрос XPath Products[@Discontinued=true()] эквивалентен выражению SQL "Products.Discontinued is not null", а не выражению SQL "Products.Discontinued = 1". Чтобы запрос был эквивалентен последнему из приведенных выражений SQL, набор узлов сначала надо преобразовать в тип, отличный от boolean, например, number. Например, Products[number(@Discontinued) = true()].

Большинство операторов реализовано так, что они возвращают TRUE, если их результат равен TRUE хотя бы для одного (любого) узла набора, поэтому они возвращают FALSE, если набор узлов пуст. Таким образом, если набор узлов A пуст, оба оператора A = B и A != B вернут FALSE, а not(A=B) и not(A!=B) — TRUE.

Обычно атрибут или элемент, сопоставленный столбцу, существует, если значение этого столбца в базе данных не равно null. Элементы, сопоставляемые строкам, существуют, если есть хотя бы один из их дочерних элементов. Дополнительные сведения см. в разделах Использование заметки sql:relation (схема XDR) и Использование заметки sql:field (схема XDR).

ПримечаниеПримечание

Элементы с аннотацией is-constant существуют всегда. Следовательно, предикаты XPath нельзя использовать для элементов, помеченных как is-constant. Дополнительные сведения см. в разделе Создание постоянных элементов с помощью sql:is-constant (схема XDR).

При преобразовании набора узлов в тип string или number его тип XDR, если таковой указан в аннотируемой схеме, анализируется и при необходимости служит основой для решения о преобразовании типов.

Сопоставление типов данных XDR типам данных XPath

Тип данных XPath узла является производным от типа данных XDR, указанного в схеме, как показано в следующей таблице (на примере узла BusinessEntityID).

Тип данных XDR

Эквивалентный

тип данных XPath

Использованное преобразование SQL Server

Nonebin.base64bin.hex

Н/Д

ОтсутствуетBusinessEntityID

boolean

boolean

CONVERT(bit, BusinessEntityID)

number, int, float,i1, i2, i4, i8,r4, r8ui1, ui2, ui4, ui8

number

CONVERT(float(53), BusinessEntityID)

id, idref, idrefsentity, entities, enumerationnotation, nmtoken, nmtokens, chardate, Timedate, Time.tz, string, uri, uuid

строка

CONVERT(nvarchar(4000), BusinessEntityID, 126)

fixed14.4

н/д (в XPath нет типа данных, эквивалентного типу fixed14.4 XDR)

CONVERT(money, BusinessEntityID)

date

строка

LEFT(CONVERT(nvarchar(4000), BusinessEntityID, 126), 10)

time

time.tz

строка

SUBSTRING(CONVERT(nvarchar(4000), BusinessEntityID, 126), 1 + CHARINDEX(N'T', CONVERT(nvarchar(4000), BusinessEntityID, 126)), 24)

Преобразования даты и времени применяются к значениям, хранящимся в базе данных с использованием типов SQL Serverdatetime или string. Следует заметить, что тип данных SQL Serverdatetime не использует тип timezone и имеет меньшую точность, чем тип данных XML time. Чтобы включить тип данных timezone или более высокую точность, следует хранить данные в SQL Server с использованием типа string.

При преобразовании узла из типа данных XDR в тип данных XPath иногда требуются дополнительные преобразования (из одного типа XPath в другой тип XPath). В качестве примера рассмотрим следующий запрос XPath:

(@m + 3) = 4

Если @m имеет тип XDR fixed14.4, то преобразование из типа XDR в тип XPath проводится следующим образом:

CONVERT(money, m)

В данном случае узел m преобразуется из типа fixed14.4 в тип money. Однако для прибавления 3 требуется дополнительное преобразование:

CONVERT(float(CONVERT(money, m))

Выражение XPath вычисляется следующим образом:

CONVERT(float(CONVERT(money, m)) + CONVERT(float(53), 3) = CONVERT(float(53), 3)

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

 

Х неизвестен

X является string

X является number

X является boolean

string(X)

CONVERT (nvarchar(4000), X, 126)

-

CONVERT (nvarchar(4000), X, 126)

CASE WHEN X THEN N'true' ELSE N'false' END

number(X)

CONVERT (float(53), X)

CONVERT (float(53), X)

-

CASE WHEN X THEN 1 ELSE 0 END

boolean(X)

-

LEN(X) > 0

X != 0

-

Примеры

А. Преобразование типа данных в запросе XPath

В следующем запросе XPath к аннотированной схеме XSD запрос выбирает все узлы Employee, у которых атрибут BusinessEntityID имеет значение E-1, где «E-» — префикс, указанный с помощью заметки sql:id-prefix.

Employee[@BusinessEntityID="E-1"]

Предикат в запросе эквивалентен следующему выражению SQL:

N'E-' + CONVERT(nvarchar(4000), Employees.BusinessEntityID, 126) = N'E-1'

Поскольку BusinessEntityID относится к одному из типов данных id (idref, idrefs, nmtoken, nmtokens и так далее) в схеме XSD, BusinessEntityID преобразуется в тип XPath string с помощью описанных ранее правил преобразования.

CONVERT(nvarchar(4000), Employees.BusinessEntityID, 126)

К строке добавляется префикс «E-», а результат затем сравнивается с N'E-1'.

Б. Несколько преобразований типов данных в запросе XPath

Рассмотрим следующий запрос XPath к схеме XSD с заметками: OrderDetail[@UnitPrice * @OrderQty > 98]

Этот запрос возвращает все дочерние элементы <OrderDetail>, удовлетворяющие предикату @UnitPrice * @OrderQty > 98. Если атрибут UnitPrice в аннотированной схеме аннотирован типом данных fixed14.4, этот предикат эквивалентен следующему выражению SQL:

CONVERT(float(53), CONVERT(money, OrderDetail.UnitPrice)) * CONVERT(float(53), OrderDetail.OrderQty) > CONVERT(float(53), 98)

В ходе преобразований значений в запросе XPath сначала тип данных XDR преобразуется в тип данных XPath. Поскольку тип данных XSD атрибута UnitPrice — fixed14.4, согласно приведенной выше таблице, сначала применяется следующее преобразование:

CONVERT(money, OrderDetail.UnitPrice)) 

Поскольку арифметические операторы преобразуют операнды к типу XPath number, применяется второе преобразование (от одного типа данных XPath к другому), в результате которого значение преобразуется к типу float(53) (тип float(53) близок к типу данных XPath number):

CONVERT(float(53), CONVERT(money, OrderDetail.UnitPrice)) 

Если у атрибута OrderQty нет типа данных XSD, то этот атрибут преобразуется к типу XPath number за один шаг:

CONVERT(float(53), OrderDetail.OrderQty)

Подобным же образом значение 98 преобразуется к типу данных XPath number:

CONVERT(float(53), 98)
ПримечаниеПримечание

Если используемый в схеме тип данных XSD несовместим с базовым типом SQL Server в базе данных или нужное преобразование типа данных XPath невозможно выполнить, SQL Server может вернуть ошибку. Например, если атрибут BusinessEntityID имеет заметку id-prefix, конструкция XPath Employee[@BusinessEntityID=1] вызовет ошибку, поскольку атрибут BusinessEntityID имеет заметку id-prefix и его нельзя преобразовать в number.