SQL 当架构数据类型为字符串时,服务器 xquery 求和转换错误

SQL Server xquery sum cast error when schema data type is string

尝试 运行 在 SQL Server 2014 中执行此操作以便对 "UserData" 中的所有值求和 xml:

IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'SC')
    DROP XML SCHEMA COLLECTION SC 
go
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
go
Declare @xml xml(SC)
set @xml=   '<UserData>
                <Item Key="CONVERTED_PAGES_1" Type="CONVERTED_PAGES">
                    <Value>2</Value>
                </Item>
                <Item Key="CONVERTED_PAGES_2" Type="CONVERTED_PAGES">
                    <Value>4</Value>
                </Item>
            </UserData>'

Select @xml.value('sum(/UserData/Item[@Type="CONVERTED_PAGES"]/Value)','int') as Sum

并出现以下错误:

Msg 9308, Level 16, State 1, Line 16 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 *'.

我尝试将 select 更改为以下内容:

Select @xml.value('sum(/UserData/Item[@Type="CONVERTED_PAGES"]/Value cast as xs:int?)','int') as Sum

但后来我明白了:

Msg 2365, Level 16, State 1, Line 16 XQuery [value()]: Cannot explicitly convert from 'xs:string *' to 'xs:int ?'

在这种情况下我无法更改 xml 模式,但我想我可以强制转换以执行此操作(因为我知道在我的情况下所有值都是 int)。如有任何建议,我们将不胜感激!

xquery sum 聚合要求输入为数字。目前它在您的 XSD 中被定义为 string。要使其正常工作,您有以下三种选择:


选项 1:

您更改架构以强制 "value" 为 int。而不是下面的第一行,使用第二行。 (两个语句之间的差异用“|||||||”突出显示。)

查询 1:

CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
                                                                                                                                                                                                                                                                                                                                                                                                          |||||||              
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:integer" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'

选项 2:

如果更改 XSD 不是一个选项,您还可以使用 T-SQL SUM 聚合而不是 xquery 聚合,如下所示:

查询 2:

IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'SC')
    DROP XML SCHEMA COLLECTION SC 
go
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>'
go
Declare @xml xml(SC)
set @xml=   '<UserData>
                <Item Key="CONVERTED_PAGES_1" Type="CONVERTED_PAGES">
                    <Value>2</Value>
                </Item>
                <Item Key="CONVERTED_PAGES_2" Type="CONVERTED_PAGES">
                    <Value>4</Value>
                </Item>
            </UserData>'

SELECT SUM(N.value('.','INT')) AS [Sum]
  FROM @xml.nodes('/UserData/Item[@Type="CONVERTED_PAGES"]/Value') AS X(N);

选项 3:

如您所见,SQL 服务器不允许我们将 XSD-typed 值转换为另一种数据类型。要解决这个问题,您可以指示 SQL 服务器忘记模式:

查询 3:

IF EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'SC')
    DROP XML SCHEMA COLLECTION SC; 
GO
CREATE XML SCHEMA COLLECTION SC AS N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"><xsd:element name="UserData"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Item" minOccurs="0" maxOccurs="unbounded"><xsd:complexType><xsd:complexContent><xsd:restriction base="xsd:anyType"><xsd:sequence><xsd:element name="Value" type="xsd:string" /><xsd:any minOccurs="0" /></xsd:sequence><xsd:attribute name="Key" type="xsd:string" /><xsd:attribute name="Type" type="xsd:string" /></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:sequence></xsd:restriction></xsd:complexContent></xsd:complexType></xsd:element></xsd:schema>';
GO
DECLARE @xml XML(SC);
SET @xml=   '<UserData>
                <Item Key="CONVERTED_PAGES_1" Type="CONVERTED_PAGES">
                    <Value>2</Value>
                </Item>
                <Item Key="CONVERTED_PAGES_2" Type="CONVERTED_PAGES">
                    <Value>4</Value>
                </Item>
            </UserData>';

SELECT CAST(@xml AS XML).value('sum((/UserData/Item[@Type="CONVERTED_PAGES"]/Value ))','int') AS Sum;

注意:没有架构,您仍然无法转换(不知道为什么),但 sum 现在无需转换即可工作。




更新:

我做了更多的挖掘。尝试投射后收到的原始错误消息是:

Msg 2365, Level 16, State 1, Line 16 XQuery [value()]: Cannot explicitly convert from 'xs:string *' to 'xs:int ?'

它告诉我们不能将字符串序列转换为单个整数。

*?都是Occurrence Indicators。所以错误信息是:zero-to-many strings can't be converted to zero-to-one integer.

你的xquery /UserData/Item[@Type="CONVERTED_PAGES"]/Value returns 不止一个值,加起来我们需要逐一转换。

xquery 提供了多种方法来实现这一点,但并非所有方法都适用于 SQL 服务器。有效的使用 for-each 构造:

.value('sum(for $val in /UserData/Item[@Type="CONVERTED_PAGES"]/Value return $val cast as xs:int?)','INT');

感谢@MikaelEriksson helping me out with this