Select语句中动态SQL变量引发的XQuery [value()]问题
解决SQL XML动态获取page节点name属性的报错问题
嘿,我来帮你搞定这个问题!你遇到的'XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *''错误,核心原因是你的写法既冗余又没抓住SQL处理XML的正确姿势——咱们完全不需要用WHILE循环来逐个处理节点,而且原来的XPath表达式没有和当前行的page节点关联,导致了单值要求不满足的问题。
直接能用的正确SQL代码
DECLARE @opxml AS XML SELECT @opxml = a.[filedata] FROM [database].[dbo].[xmlfile2] a where [Id] = 9371 SELECT C.value('@name', 'NVARCHAR(MAX)') AS [Page Name], CAST('<page>' + CAST(C.query('./child::node()') AS NVARCHAR(MAX)) + '</page>' AS XML) AS [Demographics] FROM @opxml.nodes('/level1/level2/template/page') AS T(C)
原来的代码为啥出问题?
- 循环完全没必要:SQL是基于集合的语言,你用WHILE循环遍历计数器,每次循环都查询所有
<page>节点,结果就是循环几次就重复输出几次所有page行,完全是冗余操作。 - XPath逻辑错误:你写的
(/level1/level2/template/page/@name)[sql:variable("@Counter")]是从XML根节点全局找第N个page的name,不是当前行对应的那个page节点的name——这就导致所有行都会显示同一个name(比如@Counter=1时,所有行都是第一个page的名字),这就是你当前结果里两行都是Test Page Name的原因,同时当计数器和节点位置不匹配时,还会触发单值要求的报错。
正确代码的逻辑拆解
@opxml.nodes('/level1/level2/template/page') AS T(C):这行把XML里所有的<page>节点拆成单独的行,每一行的C就代表一个独立的<page>节点。C.value('@name', 'NVARCHAR(MAX)'):直接读取当前<page>节点的name属性,因为C本身就是单个节点,所以value()能拿到唯一值,不会触发报错。CAST('<page>' + CAST(C.query('./child::node()') AS NVARCHAR(MAX)) + '</page>' AS XML):提取当前<page>节点的所有子节点,重新包裹成<page>标签,刚好得到你需要的Demographics内容。
运行这段代码后,就能得到你期望的结果:
期望结果
| Page Name | Demographics |
|---|---|
| Test Page Name | <page Cid="1" name="Test Page Name" colour="-3355393"><image Cid="8" x="432" y="8" w="148" h="95" KeyImage="32861" Ratio="y" /><formattedText Cid="14" x="9" y="22" w="253" h="38"><p><p>Text</p></p> </formattedText></page> |
| Properties | <page><formattedText Cid="7" x="200" y="148" w="208" h="228"><p><p> <t>Created by </t><t b="b">Joe Bloggs</t></p><p /><p><t>Date published 30/05/2017</t> </p></formattedText></page> |
内容的提问来源于stack exchange,提问作者badgerless
相关产品推荐
相关产品推荐

