如何在SQL查询中配置多XML命名空间以兼容不同版本的SSRS/SQL
解决SSRS跨版本XML命名空间查询问题
没问题,这其实很好解决!不同SSRS版本的报表定义用了不同的XML命名空间,我们只需要调整查询逻辑,让它能匹配这些不同的命名空间就行,下面给你两种可行的方案:
方案一:指定多个命名空间精准匹配
这种方法会明确声明你需要兼容的所有SSRS版本命名空间,然后在XPath中同时匹配这些命名空间下的节点,精准覆盖你指定的版本:
WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition' AS rd2016, 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' AS rd2010, 'http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition' AS rd2008, 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition ) SELECT CATDATA.Name AS ReportName, CATDATA.Path AS ReportPathLocation, xmlcolumn.value('(@Name)[1]', 'VARCHAR(250)') AS DataSetName, xmlcolumn.value('(Query/DataSourceName)[1]','VARCHAR(250)') AS DataSoureName, xmlcolumn.value('(Query/CommandText)[1]','VARCHAR(2500)') AS CommandText FROM ( SELECT C.Name, C.Path, CONVERT(XML,CONVERT(VARBINARY(MAX),C.Content)) AS reportXML FROM ReportServer.dbo.Catalog C WHERE C.Content is not null AND C.Type = 2 ) CATDATA CROSS APPLY reportXML.nodes(' /rd2016:Report/rd2016:DataSets/rd2016:DataSet | /rd2010:Report/rd2010:DataSets/rd2010:DataSet | /rd2008:Report/rd2008:DataSets/rd2008:DataSet ') xmltable ( xmlcolumn ) WHERE xmlcolumn.value('(Query/CommandText)[1]','VARCHAR(500)') LIKE '%sp_%' ORDER BY CATDATA.Name
代码说明:
- 我们给每个SSRS版本的命名空间都分配了一个别名(比如
rd2016对应2016版本) - 在
nodes()方法中,用XPath的|操作符来合并三个命名空间下的DataSet节点路径,这样不管报表是哪个版本生成的,都会被匹配到 - 保留了你原来的查询逻辑,只是扩展了节点匹配的范围
方案二:忽略命名空间通用匹配
如果你想兼容所有SSRS版本(包括未来可能的版本,只要节点名称不变),可以用local-name()函数直接匹配节点的本地名称,完全忽略命名空间的差异,这种方法更通用:
SELECT CATDATA.Name AS ReportName, CATDATA.Path AS ReportPathLocation, xmlcolumn.value('(@Name)[1]', 'VARCHAR(250)') AS DataSetName, xmlcolumn.value('(*[local-name()="Query"]/*[local-name()="DataSourceName"])[1]','VARCHAR(250)') AS DataSoureName, xmlcolumn.value('(*[local-name()="Query"]/*[local-name()="CommandText"])[1]','VARCHAR(2500)') AS CommandText FROM ( SELECT C.Name, C.Path, CONVERT(XML,CONVERT(VARBINARY(MAX),C.Content)) AS reportXML FROM ReportServer.dbo.Catalog C WHERE C.Content is not null AND C.Type = 2 ) CATDATA CROSS APPLY reportXML.nodes(' /*[local-name()="Report"]/*[local-name()="DataSets"]/*[local-name()="DataSet"] ') xmltable ( xmlcolumn ) WHERE xmlcolumn.value('(*[local-name()="Query"]/*[local-name()="CommandText"])[1]','VARCHAR(500)') LIKE '%sp_%' ORDER BY CATDATA.Name
代码说明:
local-name()函数会返回节点的名称(不包含命名空间前缀或URL),所以不管节点属于哪个命名空间,只要名称是Report、DataSets等,都会被匹配到- 不需要声明任何命名空间,代码更简洁,适配范围更广
额外提示:
- 注意
CommandText的长度:你原来用了VARCHAR(500),如果存储过程调用的语句很长,可能会被截断,建议改成VARCHAR(MAX)或者NVARCHAR(MAX)更保险 - 测试的时候可以先去掉
WHERE子句,查看返回的所有报表数据集,确认是否覆盖了不同版本的报表,确保没有遗漏
内容的提问来源于stack exchange,提问作者SarahChapman
相关产品推荐
相关产品推荐

