类型系统 (XQuery)
XQuery 对于架构类型是强类型语言,对于非类型化的数据是弱类型语言。预定义的 XQuery 类型包括:
- http://www.w3.org/2001/XMLSchema 命名空间中 XML 架构的内置类型。
- http://www.w3.org/2004/07/xpath-datatypes 命名空间中定义的类型。
本主题还说明了下列内容:
- 节点的类型化值与字符串值。
- 数据函数 (XQuery) 和字符串函数 (XQuery)。
- 匹配由表达式返回的序列类型。
XML 架构的内置类型
XML 架构的内置类型具有预定义的命名空间前缀 xs。例如,xs:integer 和 xs:string。所有这些内置类型都支持。您可以在创建 XML 架构集合时使用这些类型。
当查询类型化的 XML 时,节点的静态和动态类型由与正被查询的列或变量相关联的 XML 构架集合确定。有关静态和动态类型的详细信息,请参阅表达式上下文和查询计算 (XQuery)。例如,以下查询针对类型化 xml 列 (Instructions
) 指定。表达式使用 instance of
验证返回的 LotSize
属性的类型化值是 xs:decimal
类型。
SELECT Instructions.query('
DECLARE namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
data(/AWMI:root[1]/AWMI:Location[@LocationID=10][1]/@LotSize)[1] instance of xs:decimal
') AS Result
FROM Production.ProductModel
WHERE ProductModelID=7
此类型化信息是由与该列关联的 XML 架构集合提供的。有关详细信息,请参阅 AdventureWorks 数据库中的 xml 数据类型表示形式。
在 XPath 数据类型命名空间中定义的类型
在 http://www.w3.org/2004/07/xpath-datatypes 命名空间中定义的类型具有预定义的前缀 xdt。这些类型的限制条件如下:
- 在创建 XML 架构集合时无法使用这些类型。这些类型在 XQuery 类型系统中用于静态类型化。您可以在 xdt 命名空间中将其转换为原子类型,如 xdt:untypedAtomic。
- 查询非类型化的 XML 时,元素节点的静态类型和动态类型都是 xdt:untyped,属性值的类型是 xdt:untypedAtomic。query() 方法的结果将生成非类型化的 XML。这意味着 XML 节点将分别作为 xdt:untyped 和 xdt:untypedAtomic 返回。
- 不支持 xdt:dayTimeDuration 和 xdt:yearMonthDuration 类型。
在下面的示例中,查询针对非类型化的 xml 变量指定。表达式 data(/a[1]
) 返回一个原子值序列。data()
函数返回元素 <a>
的类型化值。由于被查询的 XML 是非类型化的,因此返回值的类型是 xdt:untypedAtomic
。因此,instance of
将返回 True。
DECLARE @x xml
SET @x='<a>20</a>'
SELECT @x.query( 'data(/a[1]) instance of xdt:untypedAtomic' )
在下面的示例中,表达式 (/a[1]
) 返回元素 <a>
的序列,而不是检索类型化值。instance of
表达式使用元素测试来验证由此表达式返回的值是 xdt:untyped type
的元素节点。
DECLARE @x xml
SET @x='<a>20</a>'
-- Is this an element node whose name is "a" and type is xdt:untyped.
SELECT @x.query( '/a[1] instance of element(a, xdt:untyped?)')
-- Is this an element node of type xdt:untyped.
SELECT @x.query( '/a[1] instance of element(*, xdt:untyped?)')
-- Is this an element node?
SELECT @x.query( '/a[1] instance of element()')
注意: |
---|
当查询类型化的 XML 实例并且查询表达式包含父轴时,所得到的节点的静态类型信息将不再可用。但是,动态类型仍然与这些节点关联。 |
类型化值与字符串值
每个节点都带有类型化值和字符串值。对于类型化的 XML 数据,类型化值的类型是与正被查询的列或变量相关联的 XML 架构集合提供的。对于非类型化的 XML 数据,类型化值的类型是 xdt:untypedAtomic。
您可以使用 data() 或 string() 函数来检索节点的值:
在下列 XML 架构集合中,定义了整数类型的 <root
> 元素:
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="root" type="integer"/>
</schema>'
GO
在下面的示例中,表达式首先检索 /root[1]
的类型化值,然后向其添加 3
。
DECLARE @x xml(SC)
SET @x='<root>5</root>'
SELECT @x.query('data(/root[1]) + 3')
在下一个示例中,表达式将失败,因为表达式中的 string(/root[1])
返回字符串类型值。然后此值将传递给只接受数值类型值作为操作数的算术运算符。
-- Fails because the argument is string type (must be numeric primitive type).
DECLARE @x xml(SC)
SET @x='<root>5</root>'
SELECT @x.query('string(/root[1]) + 3')
下面的示例计算 LaborHours
属性的总计。data()
函数从产品型号的所有 <Location
> 元素中检索 LaborHours
属性的类型化值。根据与 Instruction
列关联的 XML 架构,LaborHours
的类型是 xs:decimal。
SELECT Instructions.query('
DECLARE namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
sum(data(//AWMI:Location/@LaborHours))
') AS Result
FROM Production.ProductModel
WHERE ProductModelID=7
此查询将返回结果 12.75。
注意: |
---|
在此示例中显式使用 data() 函数只是为了供举例说明。如果不指定此函数,sum() 将隐式应用 data() 函数来提取节点的类型化值。 |
序列类型匹配
XQuery 表达式的值始终是零序列或多项序列。一个项可以是一个原子值,也可以是一个节点。序列类型匹配是指将查询表达式返回的序列类型与特定类型进行匹配的能力。例如:
- 如果表达式值是原子值,则您可能希望知道该值是整数类型、小数类型还是字符串类型。
- 如果表达式的值是一个 XML 节点,则您可能希望知道此节点是注释节点、处理指令节点还是文本节点。
- 您可能希望知道表达式返回的是 XML 元素还是特定名称和类型的属性节点。
您可以在序列类型匹配时使用 instance of
布尔运算符。有关 instance of
表达式的详细信息,请参阅SequenceType 表达式 (XQuery)。
比较表达式返回的原子值类型
如果表达式返回的是原子值序列,则您可能必须弄清序列中值的类型。下列示例说明如何使用序列类型语法来估计表达式返回的原子值类型。
示例 A
可以在序列类型表达式中使用 empty() 序列类型,以确定指定的表达式返回的序列是否为空序列。
在下面的示例中,XML 架构允许 <root
> 元素为空:
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="root" nillable="true" type="byte"/>
</schema>'
GO
现在,如果类型化的 XML 实例为 <root
> 元素指定值,则 instance of empty()
将返回 False。
DECLARE @var XML(SC1)
SET @var = '<root>1</root>'
-- The following returns False
SELECT @var.query('data(/root[1]) instance of empty() ')
GO
如果 <root
> 元素在实例中为空,则它的值将是一个空序列并且 instance of empty()
将返回 True。
DECLARE @var XML(SC)
SET @var = '<root xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />'
SELECT @var.query('data(/root[1]) instance of empty() ')
GO
示例 B
有时,您可能希望在进行处理前估计表达式返回的序列类型。例如,您可能具有一个在 XML 架构中定义为联合类型的节点。在下面的示例中,集合中的 XML 架构将属性 a
定义为联合类型,该类型的值可以是十进制值,也可以是字符串类型。
-- Drop schema collection if it exists.
-- DROP XML SCHEMA COLLECTION SC.
-- GO
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="root">
<complexType>
<sequence/>
<attribute name="a">
<simpleType>
<union memberTypes="decimal string"/>
</simpleType>
</attribute>
</complexType>
</element>
</schema>'
GO
处理类型化 XML 实例之前,您可能需要了解属性 a
的值类型。在下面的示例中,属性 a
的值是十进制类型。因此,instance of xs:decimal
将返回 True。
DECLARE @var XML(SC)
SET @var = '<root a="2.5"/>'
SELECT @var.query('data((/root/@a)[1]) instance of xs:decimal')
GO
现在,将属性 a
的值更改为字符串类型。instance of xs:string
将返回 True。
DECLARE @var XML(SC)
SET @var = '<root a="Hello"/>'
SELECT @var.query('data((/root/@a)[1]) instance of xs:string')
GO
示例 C
此示例说明基数在序列表达式中的作用。下列 XML 架构定义了 <root
> 元素,该元素属于字节类型且不能为空。
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema">
<element name="root" nillable="true" type="byte"/>
</schema>'
GO
在下列查询中,由于表达式返回的是单字节类型,因此 instance of
将返回 True。
DECLARE @var XML(SC)
SET @var = '<root>111</root>'
SELECT @var.query('data(/root[1]) instance of xs:byte ')
GO
如果使 <root
> 元素为空,则它的值将是一个空序列。也就是说,表达式 /root[1]
将返回一个空序列。因此,instance of xs:byte
将返回 False。注意,在这种情况下默认基数为 1。
DECLARE @var XML(SC)
SET @var = '<root xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></root>'
SELECT @var.query('data(/root[1]) instance of xs:byte ')
GO
-- result = false
如果通过添加出现指示符 (?
) 来指定基数,则序列表达式将返回 True。
DECLARE @var XML(SC)
SET @var = '<root xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></root>'
SELECT @var.query('data(/root[1]) instance of xs:byte? ')
GO
-- result = true
注意,在序列类型表达式中的测试分两个阶段完成:
- 测试先确定表达式类型是否与指定的类型匹配。
- 如果匹配,测试将确定表达式返回的项数是否与指定的出现指示符匹配。
如果两种情况都匹配,instance of
表达式将返回 True。
示例 D
在下面的示例中,针对 AdventureWorks 数据库中 xml 类型的 Instructions 列指定了一个查询。因为它具有关联的架构,因此是类型化 XML 列。有关详细信息,请参阅 AdventureWorks 数据库中的 xml 数据类型表示形式。XML 架构定义整数类型的 LocationID
属性。因此,在序列表达式中,instance of xs:integer?
将返回 True。
SELECT Instructions.query('
declare namespace AWMI="https://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelManuInstructions";
data(/AWMI:root[1]/AWMI:Location[1]/@LocationID) instance of xs:integer?') as Result
FROM Production.ProductModel
WHERE ProductModelID = 7
比较表达式返回的节点类型
如果表达式返回节点序列,则您可能必须弄清此序列中节点的类型。下列示例说明如何使用序列类型语法估计表达式返回的节点类型。您可以使用下列序列类型:
- item() - 与序列中的任意项匹配。
- node() - 确定序列是否为节点。
- processing-instruction() - 确定表达式是否返回处理指令。
- comment() - 确定表达式是否返回注释。
- document-node() - 确定表达式是否返回文档节点。
下列示例说明这些序列类型。
示例 A
在此示例中,针对非类型化的 XML 变量执行了若干查询。这些查询说明序列类型的使用方法。
DECLARE @var XML
SET @var = '<?xml-stylesheet href="someValue" type="text/xsl" ?>
<root>text node
<!-- comment 1 -->
<a>Data a</a>
<!-- comment 2 -->
</root>'
在第一个查询中,表达式返回元素 <a
> 的类型化值。在第二个查询中,表达式返回元素 <a
>。两个返回值都是项。因此,两个查询都返回 True。
SELECT @var.query('data(/root[1]/a[1]) instance of item()')
SELECT @var.query('/root[1]/a[1] instance of item()')
下列三个查询中的所有 XQuery 表达式都返回 <root
> 元素节点子级。因此,序列类型表达式 instance of node()
将返回 True,另外两个表达式 instance of text()
和 instance of document-node()
将返回 False。
SELECT @var.query('(/root/*)[1] instance of node()')
SELECT @var.query('(/root/*)[1] instance of text()')
SELECT @var.query('(/root/*)[1] instance of document-node()')
在下列查询中,instance of document-node()
表达式将返回 True,因为 <root
> 元素的父级是文档节点。
SELECT @var.query('(/root/..)[1] instance of document-node()') -- true
在下列查询中,表达式检索 XML 实例中的第一个节点。由于此节点是处理指令节点,因此 instance of processing-instruction()
表达式将返回 True。
SELECT @var.query('(/node())[1] instance of processing-instruction()')
实现限制
具体的限制如下:
- 不支持带有内容类型语法的 document-node() 。
- 不支持 processing-instruction(name) 语法。
元素测试
元素测试用于将表达式返回的元素节点与具有特定名称和类型的元素节点相匹配。您可以使用这些元素进行以下测试:
element ()
element(ElementName)
element(ElementName, ElementType?)
element(*, ElementType?)
属性测试
属性测试决定了表达式返回的属性是否为属性节点。您可以使用下列属性测试。
attribute()
attribute(AttributeName)
attribute(AttributeName, AttributeType)
测试示例
下面的示例说明了适合采用元素测试和属性测试的情形。
示例 A
以下 XML 架构定义 CustomerType
复杂类型,其中 <firstName
> 和 <lastName
> 元素是可选的。对于指定的 XML 实例,您可能需要确定特定客户的名字是否存在。
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="myNS" xmlns:ns="myNS">
<complexType name="CustomerType">
<sequence>
<element name="firstName" type="string" minOccurs="0"
nillable="true" />
<element name="lastName" type="string" minOccurs="0"/>
</sequence>
</complexType>
<element name="customer" type="ns:CustomerType"/>
</schema>
'
GO
DECLARE @var XML(SC)
SET @var = '<x:customer xmlns:x="myNS">
<firstName>SomeFirstName</firstName>
<lastName>SomeLastName</lastName>
</x:customer>'
下列查询使用 instance of element (firstName)
表达式确定 <customer
> 的第一个子元素是否名为 <firstName
>。在这种情况下,查询将返回 True。
SELECT @var.query('declare namespace x="myNS";
(/x:customer/*)[1] instance of element (firstName)')
GO
如果从实例中删除 <firstName
> 元素,查询将返回 False。
您也可以使用下列内容:
element(ElementName, ElementType?)
序列类型语法,如以下查询中所示。它将与名为firstName
、类型为xs:string
的空元素节点或非零元素节点进行匹配。SELECT @var.query('declare namespace x="myNS"; (/x:customer/*)[1] instance of element (firstName, xs:string?)')
element(*, type?)
序列类型语法,如以下查询中所示。它将与类型为xs:string
(名称不限)的元素节点进行匹配。SELECT @var.query('declare namespace x="myNS"; (/x:customer/*)[1] instance of element (*, xs:string?)') GO
示例 B
下列示例说明如何确定表达式返回的节点是否为具有特定名称的元素节点。此示例使用 element() 测试。
在下面的示例中,正在查询的 XML 实例中的两个 <Customer
> 元素属于两种不同的类型:CustomerType
和 SpecialCustomerType
。假定您希望知道表达式返回的 <Customer
> 元素的类型。以下 XML 架构集合定义 CustomerType
和 SpecialCustomerType
类型。
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="myNS" xmlns:ns="myNS">
<complexType name="CustomerType">
<sequence>
<element name="firstName" type="string"/>
<element name="lastName" type="string"/>
</sequence>
</complexType>
<complexType name="SpecialCustomerType">
<complexContent>
<extension base="ns:CustomerType">
<sequence>
<element name="Age" type="int"/>
</sequence>
</extension>
</complexContent>
</complexType>
<element name="customer" type="ns:CustomerType"/>
</schema>
'
GO
此 XML 架构集合用于创建类型化的 xml 变量。分配给此变量的 XML 实例具有两个属于两种不同类型的 <customer
> 元素。第一个元素属于 CustomerType
类型,第二个元素属于 SpecialCustomerType
类型。
DECLARE @var XML(SC)
SET @var = '
<x:customer xmlns:x="myNS">
<firstName>FirstName1</firstName>
<lastName>LastName1</lastName>
</x:customer>
<x:customer xsi:type="x:SpecialCustomerType" xmlns:x="myNS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<firstName> FirstName2</firstName>
<lastName> LastName2</lastName>
<Age>21</Age>
</x:customer>'
在以下查询中,由于表达式返回的第一个客户元素不属于 SpecialCustomerType
类型,因此,instance of element (*, x:SpecialCustomerType ?)
表达式返回 False。
SELECT @var.query('declare namespace x="myNS";
(/x:customer)[1] instance of element (*, x:SpecialCustomerType ?)')
如果更改上一个查询的表达式并检索第二个 <customer
> 元素 (/x:customer)[2]
),则 instance of
将返回 True。
示例 C
此示例使用属性测试。下列 XML 架构定义了带有 CustomerID 属性和 Age 属性的 CustomerType 复杂类型。Age 属性是可选的。对于特定的 XML 实例,您可能希望确定 <customer
> 元素中是否存在 Age 属性。
CREATE XML SCHEMA COLLECTION SC AS N'
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="myNS" xmlns:ns="myNS">
<complexType name="CustomerType">
<sequence>
<element name="firstName" type="string" minOccurs="0"
nillable="true" />
<element name="lastName" type="string" minOccurs="0"/>
</sequence>
<attribute name="CustomerID" type="integer" use="required" />
<attribute name="Age" type="integer" use="optional" />
</complexType>
<element name="customer" type="ns:CustomerType"/>
</schema>
'
GO
由于正在查询的 XML 实例中具有名称为 Age
的属性节点,因此,以下查询返回 True。此表达式中使用了 attribute(Age)
属性测试。由于属性是无序的,查询使用 FLWOR 表达式检索所有属性,然后使用 instance of
表达式测试每个属性。此示例首先创建一个 XML 架构集合,以创建类型化的 xml 变量。
DECLARE @var XML(SC)
SET @var = '<x:customer xmlns:x="myNS" CustomerID="1" Age="22" >
<firstName>SomeFName</firstName>
<lastName>SomeLName</lastName>
</x:customer>'
SELECT @var.query('declare namespace x="myNS";
FOR $i in /x:customer/@*
RETURN
IF ($i instance of attribute (Age)) THEN
"true"
ELSE
()')
GO
如果从实例中删除了可选的 Age
属性,则上述查询将返回 False。
您可以在属性测试中指定属性名称和类型 (attribute(name,type)
)。
SELECT @var.query('declare namespace x="myNS";
FOR $i in /x:customer/@*
RETURN
IF ($i instance of attribute (Age, xs:integer)) THEN
"true"
ELSE
()')
另外,也可以指定 attribute(*, type)
序列类型语法。如果属性类型与指定类型匹配(名称不限),则此序列类型语法将与属性节点匹配。
实现限制
具体的限制如下:
- 在元素测试中,类型名称后面必须跟随出现指示符 (?)。
- 不支持 element(ElementName, TypeName)。
- 不支持 element(*, TypeName)。
- 不支持 schema-element()。
- 不支持 schema-attribute(AttributeName)。
- 不支持显式查询 xsi:type 或 xsi:nil。
请参阅
概念
SequenceType 表达式 (XQuery)
XQuery 基础知识