Cross apply返回过多行问题咨询(含testXML表DDL与插入语句)
解决CROSS APPLY返回过多行的问题
我来帮你搞定这个CROSS APPLY返回行数超预期的问题!首先咱们先把你的表结构和插入数据贴出来,方便对照分析:
CREATE TABLE [testXML] ( [scheduleid] [uniqueidentifier] primary key, [XMLData1] [xml] NULL ) INSERT INTO testXML ([scheduleid],XMLData1) VALUES ('88888888-DDDD-4444-AAAA-666666666666','<ArrayOfRDData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <RDData h="Title" o="2017-11-02T16:00:00Z" p="212" q="000000cb-0000-0000-0000-000000000000" an="203" > <rps> <rp s="00a566e2-0000-0000-0000-000000000000" ag="1" i="0" j="0" /> <!-- 这里应该还有更多rp节点或者其他内容 --> </rps> </RDData> </ArrayOfRDData>')
常见原因分析
CROSS APPLY返回过多行,大多和XML节点的嵌套结构以及你的查询逻辑有关:
- 嵌套节点展开导致的正常多行:如果你的XML里一个
<RDData>下有多个<rp>节点,用CROSS APPLY逐层展开时,会把每个<rp>都拆成单独一行,这其实是正常的解析结果,但如果你误以为只会返回一行,就会觉得行数过多。 - 错误的节点关联导致笛卡尔积:如果你的查询里多次CROSS APPLY根节点下的同一层级(比如同时展开两次
/ArrayOfRDData/RDData),就会产生笛卡尔积,行数直接爆炸成n²。 - 未注意到XML里有多个同级节点:比如你的XML里可能不止一个
<RDData>,而你没意识到,展开后自然行数变多。
针对性解决方案
1. 先确认XML节点数量
先搞清楚你的XML里到底有多少个目标节点,比如查一下有多少个<RDData>:
SELECT t.scheduleid, COUNT(*) AS RDData_总数 FROM testXML t CROSS APPLY t.XMLData1.nodes('/ArrayOfRDData/RDData') AS RD(rd) GROUP BY t.scheduleid
再查每个<RDData>下有多少个<rp>:
SELECT t.scheduleid, rd.value('@h', 'NVARCHAR(100)') AS RD_Title, COUNT(*) AS RP_数量 FROM testXML t CROSS APPLY t.XMLData1.nodes('/ArrayOfRDData/RDData') AS RD(rd) CROSS APPLY rd.nodes('rps/rp') AS RP(rp) GROUP BY t.scheduleid, rd.value('@h', 'NVARCHAR(100)')
2. 修正CROSS APPLY的层级关联
如果是因为错误关联导致笛卡尔积,要确保下一层的CROSS APPLY是基于上一层的节点,而不是重新从根节点开始:
-- 正确的逐层展开写法:每个rp都属于对应的RDData SELECT t.scheduleid, rd.value('@h', 'NVARCHAR(100)') AS Title, rd.value('@o', 'DATETIME') AS 时间, rp.value('@s', 'UNIQUEIDENTIFIER') AS RP_ID, rp.value('@ag', 'INT') AS RP_AG FROM testXML t CROSS APPLY t.XMLData1.nodes('/ArrayOfRDData/RDData') AS RD(rd) -- 这里用rd.nodes,而不是t.XMLData1.nodes,确保关联到父节点 CROSS APPLY rd.nodes('rps/rp') AS RP(rp)
3. 按需过滤或限制行数
如果你不需要所有子节点,只想取每个<RDData>下的第一个<rp>,可以用节点索引:
SELECT t.scheduleid, rd.value('@h', 'NVARCHAR(100)') AS Title, rp.value('@s', 'UNIQUEIDENTIFIER') AS 第一个RP_ID FROM testXML t CROSS APPLY t.XMLData1.nodes('/ArrayOfRDData/RDData') AS RD(rd) -- 只取第一个rp节点 CROSS APPLY rd.nodes('rps/rp[1]') AS RP(rp)
如果有些<RDData>没有<rp>节点,你想保留这些行,就把CROSS APPLY换成OUTER APPLY。
4. 聚合子节点数据
如果需要把同一<RDData>下的所有<rp>数据聚合起来(比如拼接成字符串),可以用FOR XML PATH:
SELECT t.scheduleid, rd.value('@h', 'NVARCHAR(100)') AS Title, STUFF( (SELECT ',' + rp.value('@s', 'NVARCHAR(50)') FROM rd.nodes('rps/rp') AS RP(rp) FOR XML PATH('')), 1, 1, '' ) AS 所有RP_ID FROM testXML t CROSS APPLY t.XMLData1.nodes('/ArrayOfRDData/RDData') AS RD(rd)
内容的提问来源于stack exchange,提问作者Ron Gordon
相关产品推荐
相关产品推荐

