Tipos de dados XPath (SQLXML 4.0)
O Microsoft SQL Server, XPath e Esquema XML (XSD) têm tipos de dados bem diferentes. Por exemplo, o XPath não tem tipos de dados de data ou inteiros, mas o SQL Server e o XSD têm muitos. O XSD usa precisão de nanossegundos para valores de tempo, e o SQL Server usa precisão de no máximo 1/300 segundo. Consequentemente, o mapeamento de um tipo de dados para outro nem sempre é possível. Para obter mais informações sobre como mapear tipos de dados do SQL Server para tipos de dados XSD, consulte Coerções de tipo de dados e a anotação de sql:datatype (SQLXML 4.0).
O XPath tem três tipos de dados: string, number e boolean. O tipo de dados number sempre é um ponto flutuante de precisão dupla IEEE 754. O tipo de dados float(53) do SQL Server é o mais semelhante ao number do XPath. Entretanto, o float(53) não é exatamente igual ao IEEE 754. Por exemplo, nem NaN (não é um número) nem infinidade é usado. Tentar converter uma cadeia de caracteres não numérica em number e tentar dividir por zero resulta em erro.
Conversões do XPath
Quando você usa uma consulta do XPath como OrderDetail[@UnitPrice > "10.0"], conversões de tipos de dados implícitas e explícitas podem alterar sutilmente o significado da consulta. Por isso, é importante entender como são implementados os tipos de dados XPath. A especificação da linguagem XPath, ou seja, a XML Path Language (XPath) version 1.0 W3C Proposed Recommendation 8 October 1999, pode ser encontrada no site do W3C em http://www.w3.org/TR/1999/PR-xpath-19991008.html.
Os operadores de XPath são divididos em quatro categorias:
Operadores booleanos (e, ou)
Operadores relacionais (<, >, <=, >=)
Operadores de igualdade (=, !=)
Operadores aritméticos (+, -, *, div, mod)
Cada categoria de operador converte os respectivos operandos de forma diferente. Os operadores de XPath convertem implicitamente seus operandos se necessário. Os operadores aritméticos convertem seus operandos em number e resultam em um valor numérico. Os operadores booleanos convertem seus operandos em boolean e resultam em um valor booleano. Os operadores relacionais e operadores de igualdade resultam em um valor booleano. Entretanto, eles têm diferentes regras de conversão dependendo dos tipos de dados originais dos respectivos operandos, conforme mostra esta tabela.
Operando |
Operador relacional |
Operador de igualdade |
---|---|---|
Ambos os operandos são conjuntos de nós. |
TRUE se e somente se houver um nó em um conjunto e um nó no segundo conjunto de modo que a comparação de seus valores de string seja TRUE. |
Idem. |
Um é um conjunto de nós, o outro é uma string. |
TRUE se e somente se houver um nó no conjunto de nós de modo que, quando convertido em number, a comparação dele com a string convertida em number seja TRUE. |
TRUE se e somente se houver um nó no conjunto de nós de modo que, quando convertido em string, a comparação dele com a string seja TRUE. |
Um é um conjunto de nós, o outro é uma number. |
TRUE se e somente se houver um nó no conjunto de nós de modo que, quando convertido em number, a comparação dele com a number seja TRUE. |
Idem. |
Um é um conjunto de nós, o outro é uma boolean. |
TRUE se e somente se houver um nó no conjunto de nós de modo que, quando convertido em boolean e depois em number, a comparação dele com o boolean convertido em number seja TRUE. |
TRUE se e somente se houver um nó no conjunto de nós de modo que, quando convertido em boolean, a comparação dele com a boolean seja TRUE. |
Nenhum é um conjunto de nós. |
Converta ambos os operandos em number e compare. |
Converta ambos os operandos em um tipo comum e compare. Converta em boolean se qualquer um dos dois for boolean e em number se qualquer um dos dois for number; caso contrário, converta em string. |
Observação |
---|
Como os operadores relacionais de XPath sempre convertem seus operandos em number, comparações de string não são possíveis. Para incluir comparações de data, o SQL Server 2000 oferece essa variação da especificação do XPath: quando um operador relacional compara uma string com uma string, um conjunto de nós com uma string ou um conjunto de nós de valor de cadeia de caracteres com um conjunto de nós de valor de cadeia de caracteres, é executada uma comparação de string (e não uma comparação de number). |
Conversões de conjuntos de nós
As conversões de conjuntos de nós nem sempre são intuitivas. Um conjunto de nós é convertido em string obtendo o valor da cadeia de caracteres somente do primeiro nó do conjunto. Um conjunto de nós é convertido em number convertendo-o em string e convertendo string em number. Um conjunto de nós é convertido em boolean testando sua existência.
Observação |
---|
O SQL Server não executa a seleção posicional nos conjuntos de nós: por exemplo, a consulta do XPath Customer[3] significa o terceiro cliente; não há suporte a esse tipo de seleção posicional no SQL Server. Portanto, as conversões de conjunto de nós em string ou de conjunto de nós em number conforme descritas pela especificação do XPath não são implementadas. O SQL Server usa "qualquer" semântica sempre que a especificação do XPath define "primeira" semântica. Por exemplo, com base na especificação do XPath do W3C, a consulta do XPath Order[OrderDetail/@UnitPrice > 10.0] seleciona os pedidos com o primeiro OrderDetail que tem um UnitPrice maior do que 10.0. No SQL Server, essa consulta do XPath seleciona os pedidos com qualquer OrderDetail que tenha um UnitPrice maior do que 10.0. |
A conversão em boolean gera um teste de existência; por isso, a consulta do XPath Products[@Discontinued=true()] é equivalente à expressão SQL "Products.Discontinued is not null", e não à expressão SQL "Products.Discontinued = 1". Para tornar a consulta equivalente a essa última expressão SQL, primeiro converta o conjunto de nós em um tipo não boolean, como number. Por exemplo, Products[number(@Discontinued) = true()].
Como a maioria dos operadores é definida como TRUE se é TRUE para qualquer um ou para um dos nós do conjunto, essas operações sempre avaliam como FALSE se o conjunto está vazio. Assim, se A está vazio, tanto A = B quanto A != B são FALSE, e not(A=B) e not(A!=B) são TRUE.
Normalmente, um atributo ou elemento que mapeia para uma coluna existe se o valor dessa coluna no banco de dados não é null. Elementos que mapeiam para linhas existirão se quaisquer dos filhos existirem. Para obter mais informações, consulte Usando sql:relation (Esquema XDR) e Usando sql:field (Esquema XDR).
Observação |
---|
Elementos anotados com is-constant sempre existem. Consequentemente, os predicados do XPath não podem ser usados em elementos is-constant. Para obter mais informações, consulte Criando elementos constantes usando sql:is-constant (Esquema XDR). |
Quando um conjunto de nós é convertido em string ou number, seu tipo de XDR (se houver) é inspecionado no esquema anotado e esse tipo é usado para determinar a conversão necessária.
Mapeando tipos de dados XDR para tipos de dados XPath
O tipo de dados XPath de um nó é derivado do tipo de dados XSD no esquema, conforme mostra a tabela a seguir (o nó BusinessEntityID é usado para fins meramente ilustrativos).
Tipo de dados XDR |
Tipo de dados XPath equivalente |
Conversão do SQL Server usada |
---|---|---|
Nonebin.base64bin.hex |
N/A |
NoneBusinessEntityID |
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 |
string |
CONVERT(nvarchar(4000), BusinessEntityID, 126) |
fixed14.4 |
N/D (não há nenhum tipo de dados no XPath equivalente ao tipo de dados XDR fixed14.4) |
CONVERT(money, BusinessEntityID) |
date |
string |
LEFT(CONVERT(nvarchar(4000), BusinessEntityID, 126), 10) |
time time.tz |
string |
SUBSTRING(CONVERT(nvarchar(4000), BusinessEntityID, 126), 1 + CHARINDEX(N'T', CONVERT(nvarchar(4000), BusinessEntityID, 126)), 24) |
As conversões de data e hora foram projetadas para funcionar independentemente de o valor estar armazenado no banco de dados usando o tipo de dados datetime do SQL Server ou uma string. Observe que o tipo de dados datetime do SQL Server não usa timezone e tem uma precisão menor que o tipo de dados time do XML. Para incluir o tipo de dados timezone ou precisão adicional, armazene os dados no SQL Server usando um tipo string.
Quando um nó é convertido do tipo de dados XDR no tipo de dados XPath, às vezes é necessária conversão adicional (de um tipo de dados XPath para outro tipo de dados XPath). Por exemplo, considere esta consulta do XPath:
(@m + 3) = 4
Se @m é do tipo de dados XDR fixed14.4, a conversão do tipo de dados XDR para o tipo de dados XPath será feita usando:
CONVERT(money, m)
Nessa conversão, o nó m é convertido de fixed14.4 em money. Porém, a adição do valor de 3 exige conversão adicional:
CONVERT(float(CONVERT(money, m))
A expressão do XPath é avaliada como:
CONVERT(float(CONVERT(money, m)) + CONVERT(float(53), 3) = CONVERT(float(53), 3)
Conforme mostra a tabela a seguir, essa é a mesma conversão aplicada para outras expressões do XPath (como literais ou expressões compostas).
|
X é a incógnita |
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 |
- |
Exemplos
A. Converter um tipo de dados em uma consulta do XPath
Na seguinte consulta do XPath especificada com base em um esquema XSD anotado, a consulta seleciona todos os nós Employee com o valor de atributo BusinessEntityID de E-1, onde "E-" é o prefixo especificado usando a anotação sql:id-prefix.
Employee[@BusinessEntityID="E-1"]
O predicado na consulta é equivalente à expressão SQL:
N'E-' + CONVERT(nvarchar(4000), Employees.BusinessEntityID, 126) = N'E-1'
Como BusinessEntityID é um dos valores de tipo de dados id (idref, idrefs, nmtoken, nmtokens e assim por diante) no esquema XSD, BusinessEntityID é convertido no tipo de dados XPath string usando as regras de conversão descritas anteriormente.
CONVERT(nvarchar(4000), Employees.BusinessEntityID, 126)
O prefixo "E -" é adicionado à cadeia de caracteres, e o resultado é comparado com N'E-1'.
B. Executar várias conversões de tipo de dados em uma consulta do XPath
Considere esta consulta do XPath especificada com base em um esquema XSD anotado: OrderDetail[@UnitPrice * @OrderQty > 98]
Essa consulta do XPath retorna todos os elementos <OrderDetail> que satisfazem ao predicado @UnitPrice * @OrderQty > 98. Se UnitPrice for anotado com um tipo de dados fixed14.4 no esquema anotado, esse predicado será equivalente à expressão SQL:
CONVERT(float(53), CONVERT(money, OrderDetail.UnitPrice)) * CONVERT(float(53), OrderDetail.OrderQty) > CONVERT(float(53), 98)
Ao converter os valores na consulta do XPath, ocorre a primeira conversão do tipo de dados XDR no tipo de dados XPath. Como o tipo de dados XSD de UnitPrice é fixed14.4, conforme descrito na tabela anterior, esta é a primeira conversão usada:
CONVERT(money, OrderDetail.UnitPrice))
Como os operadores aritméticos convertem os operandos no tipo de dados XPath number, a segunda conversão (de um tipo de dados XPath em outro tipo de dados XPath) é aplicada, no qual o valor é convertido em float(53) (float(53) está próximo ao tipo de dados number do XPath):
CONVERT(float(53), CONVERT(money, OrderDetail.UnitPrice))
Supondo que o atributo OrderQty não tem nenhum tipo de dados XSD, o OrderQty é convertido em um tipo de dados number do XPath em uma única conversão:
CONVERT(float(53), OrderDetail.OrderQty)
Da mesma maneira, o valor 98 é convertido no tipo de dados XPath number:
CONVERT(float(53), 98)
Observação |
---|
Se o tipo de dados XSD usado no esquema for incompatível com o tipo de dados subjacente do SQL Server no banco de dados ou se for executada uma conversão impossível de tipo de dados XPath, o SQL Server poderá retornar um erro. Por exemplo, se o atributo BusinessEntityID for anotado com uma anotação id-prefix, o XPath Employee[@BusinessEntityID=1] gerará um erro, porque BusinessEntityID tem a anotação id-prefix e não pode ser convertido em number. |