Eu tenho o seguinte código T-SQL que estou tentando fazer funcionar:
IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'TST') DROP XML SCHEMA COLLECTION TST;
GO
CREATE XML SCHEMA COLLECTION TST AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="root"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="val" minOccurs="0" maxOccurs="unbounded" type="xsd:string" /></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>';
GO
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT @xml.value('sum(//val)','INT')
O xsd
nos diz que o val
nó é do tipo string
. Portanto, a sum()
função não funcionará:
Msg 9308, Level 16, State 1, Line 7
XQuery [value()]: The argument of 'sum()' must be of a single numeric primitive type or 'http://www.w3.org/2004/07/xpath-datatypes#untypedAtomic'. Found argument of type 'xs:string *'.
Eu tentei as seguintes maneiras de fazer isso funcionar:
Opção 1 :
Uma solução alternativa é apenas se livrar do esquema xsd:
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT (CAST(@xml AS XML)).value('sum(//val)','INT')
Isso retorna o resultado correto: 6. No entanto, parece bastante grosseiro e pode nem sempre ser uma opção (por exemplo, ao lidar com índices XML).
(Não-)Opção 2 :
Uma alternativa é lançar cada valor xs:integer
antes de somá-los. No entanto, é aí que estou preso.
É fácil lançar um único elemento:
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT @xml.value('sum(xs:integer((//val)[1]))','INT')
Mas isso resulta em 1, que é o resultado errado. Isso é esperado, já que agora (por causa do [1]
) apenas o primeiro val
nó é analisado.
(Não-)Opção 3 :
Existem duas maneiras de escrever um elenco em xquery:
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT @xml.value('sum(xs:integer((//val)))','INT')
GO
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT @xml.value('sum(((//val) cast as xs:integer ?))','INT')
No entanto, ambos funcionam apenas com singletons como entrada:
Msg 2365, Level 16, State 1, Line 15
XQuery [value()]: Cannot explicitly convert from 'xs:string *' to 'xs:integer'
Msg 2365, Level 16, State 1, Line 18
XQuery [value()]: Cannot explicitly convert from 'xs:string *' to 'xs:integer ?'
(Não-)Opção 4 :
De acordo com a documentação do xquery, você deve ser capaz de fazer isso:
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT @xml.value('sum((//val)! xs:integer(.))','INT')
No entanto, isso parece não ser implementado pelo SQL Server:
Msg 2217, Level 16, State 1, Line 21
XQuery [value()]: ',' or ')' expected
Opção 5 :
Você poderia usar a .nodes()
função e resolver o problema fora do xquery:
DECLARE @xml XML(TST) = '<root><val>1</val><val>2</val><val>3</val></root>';
SELECT SUM(X.V.value('.','INT')) FROM @xml.nodes('//val') X(V)
Isso resulta no valor correto de 6. No entanto, estou procurando uma solução dentro do xquery, portanto, essa não é uma opção para mim.
Opção? :
Com isso estou sem ideias. Existe uma solução para este problema?
Adicional :
Além disso, gostaria muito de saber o que ?
significa o nesta linha, retirado de um dos exemplos acima:
SELECT @xml.value('sum(((//val)[1] cast as xs:integer ?))','INT')
O SQL Server exige isso. Você recebe este erro, se tentar sem:
Msg 9301, Level 16, State 1, Line 18
XQuery [value()]: In this version of the server, 'cast as <type>' is not available. Please use the 'cast as <type> ?' syntax.
Esse erro sugere o fato de que esse requisito é uma limitação do SQL Server. No entanto, não consegui encontrar documentação sobre seu significado em nenhum lugar.