英文:
Cannot parse SQL Server XML column when reportdefinition is "defaultfontfamily"
问题
我在SQL Server表中使用了一个XML列。有些行的XML没有正确解析,我已经找到了原因 - 对于无法读取的行,XML使用了' http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily'。我尝试将其添加为命名空间,但没有帮助。
没有问题的行具有以下XML命名空间:
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<AutoRefresh>0</AutoRefresh>
<DataSources>
...
</Report>
有问题的行具有以下XML命名空间:
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns:df="http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily" MustUnderstand="df">
<df:DefaultFontFamily>Segoe UI</df:DefaultFontFamily>
<AutoRefresh>0</AutoRefresh>
<DataSources>
...
</Report>
当xmlcolumn.values被分配时,您可以看到它何时有效 - 这些不包含额外的行<df:DefaultFontFamily>Segoe UI</df:DefaultFontFamily>。
以下是我使用的SQL。提前感谢您的帮助。
WITH XMLNAMESPACES
( DEFAULT
'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition'
, 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition
, 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily' AS df)
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 DataSourceName
,xmlcolumn.value('(Query/CommandText)[1]','VARCHAR(2500)') AS CommandText
,reportXML
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
outer APPLY reportXML.nodes('/Report/DataSets/DataSet') xmltable ( xmlcolumn )
--WHERE xmlcolumn.value('(Query/CommandText)[1]','VARCHAR(250)') LIKE '%2_%'
ORDER BY CATDATA.Name
英文:
I am using an XML column in a SQL Server table. There are certain rows where the XML is not properly parsed, and I have identified why - for rows where it cannot be read, the XML is using 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily'. I have tried adding it as a namespace, but it did not help.
Those rows with no problem have xml namespaces as:
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<AutoRefresh>0</AutoRefresh>
<DataSources>
...
Those rows with a problem have xml namespaces as:
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns:df="http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily" MustUnderstand="df">
<df:DefaultFontFamily>Segoe UI</df:DefaultFontFamily>
<AutoRefresh>0</AutoRefresh>
<DataSources>
...
You can see when it works when xmlcolumn.values are assigned - these do not contain the additional line <df:DefaultFontFamily>Segoe UI</df:DefaultFontFamily>

Here is the SQL I am using. Thank you in advance.
WITH XMLNAMESPACES
( DEFAULT
'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition'
, 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition
, 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily' AS df)
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 DataSourceName
,xmlcolumn.value('(Query/CommandText)[1]','VARCHAR(2500)') AS CommandText
,reportXML
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
outer APPLY reportXML.nodes('/Report/DataSets/DataSet') xmltable ( xmlcolumn )
--WHERE xmlcolumn.value('(Query/CommandText)[1]','VARCHAR(250)') LIKE '%2_%'
ORDER BY CATDATA.Name
答案1
得分: 3
你的XML确切内容不清晰,因为你没有展示一个完整的示例,所以我不得不猜测。似乎Report有一个不同的默认命名空间。
你有三个选择:
- 要么完全忽略命名空间,使用
*:WITH XMLNAMESPACES ( DEFAULT 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' , 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition , 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily' AS df ) SELECT C.Name AS ReportName ,C.Path AS ReportPathLocation ,xmlcolumn.value('(@Name)[1]', 'VARCHAR(250)') AS DataSetName ,xmlcolumn.value('(*:Query/*:DataSourceName/text())[1]','VARCHAR(250)') AS DataSourceName ,xmlcolumn.value('(*:Query/*:CommandText/text())[1]','VARCHAR(2500)') AS CommandText ,reportXML FROM ReportServer.dbo.Catalog C CROSS APPLY ( SELECT CONVERT(XML, CONVERT(VARBINARY(MAX), C.Content)) AS reportXML ) CATDATA OUTER APPLY reportXML.nodes('/*:Report/*:DataSets/*:DataSet') xmltable ( xmlcolumn ) WHERE C.Content is not null AND C.Type = 2 ORDER BY C.Name - 或者你可以使用多个
.nodesWITH XMLNAMESPACES ( DEFAULT 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' , 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition' AS r2016 , 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition , 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily' AS df) SELECT C.Name AS ReportName ,C.Path AS ReportPathLocation ,ISNULL( xmlcolumn1.value('(@Name)[1]', 'VARCHAR(250)'), xmlcolumn2.value('(@Name)[1]', 'VARCHAR(250)')) AS DataSetName ,ISNULL( xmlcolumn1.value('(Query/DataSourceName/text())[1]','VARCHAR(250)'), xmlcolumn2.value('(r2016:Query/r2016:DataSourceName/text())[1]','VARCHAR(250)')) AS DataSourceName ,ISNULL( xmlcolumn1.value('(Query/CommandText/text())[1]','VARCHAR(2500)'), xmlcolumn2.value('(r2016:Query/r2016:CommandText/text())[1]','VARCHAR(2500)')) AS CommandText ,reportXML FROM ReportServer.dbo.Catalog C CROSS APPLY ( SELECT CONVERT(XML, CONVERT(VARBINARY(MAX), C.Content)) AS reportXML ) CATDATA OUTER APPLY reportXML.nodes('/Report/DataSets/DataSet') xmltable1 ( xmlcolumn1 ) OUTER APPLY reportXML.nodes('/r2016:Report/r2016:DataSets/r2016:DataSet') xmltable2 ( xmlcolumn2 ) WHERE C.Content is not null AND C.Type = 2 ORDER BY C.Name
很难说哪个版本更快。忽略命名空间是慢的,但查询两次也是慢的。
注意其他改进:
- 使用
CROSS APPLY进行转换以避免嵌套。 - 在进行XQuery时使用
/text()来提高性能。 - 如果可以将列实际更改为
xml并避免转换,那将是更好的。
作为一个副注,如果你实际上想要那个WHERE,最好的方法是将其放在.nodes内部(请注意这里使用的是CROSS APPLY而不是OUTER APPLY。
CROSS APPLY reportXML.nodes('
/*Report/*:DataSets/*:DataSet
[*:Query/*:CommandText/text()[contains(., "2_")]
') xmltable ( xmlcolumn )
英文:
Your exact XML is unclear as you haven't shown us a full sample, so I've had to guess. It seems Report has a different default namespace.
You have three options:
- Either ignore the namespace completely using
*:WITH XMLNAMESPACES ( DEFAULT 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' , 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition , 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily' AS df ) SELECT C.Name AS ReportName ,C.Path AS ReportPathLocation ,xmlcolumn.value('(@Name)[1]', 'VARCHAR(250)') AS DataSetName ,xmlcolumn.value('(*:Query/*:DataSourceName/text())[1]','VARCHAR(250)') AS DataSourceName ,xmlcolumn.value('(*:Query/*:CommandText/text())[1]','VARCHAR(2500)') AS CommandText ,reportXML FROM ReportServer.dbo.Catalog C CROSS APPLY ( SELECT CONVERT(XML, CONVERT(VARBINARY(MAX), C.Content)) AS reportXML ) CATDATA OUTER APPLY reportXML.nodes('/*:Report/*:DataSets/*:DataSet') xmltable ( xmlcolumn ) WHERE C.Content is not null AND C.Type = 2 ORDER BY C.Name - Or you can use multiple
.nodesWITH XMLNAMESPACES ( DEFAULT 'http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition' , 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition' AS r2016 , 'http://schemas.microsoft.com/SQLServer/reporting/reportdesigner' AS ReportDefinition , 'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition/defaultfontfamily' AS df) SELECT C.Name AS ReportName ,C.Path AS ReportPathLocation ,ISNULL( xmlcolumn1.value('(@Name)[1]', 'VARCHAR(250)'), xmlcolumn2.value('(@Name)[1]', 'VARCHAR(250)')) AS DataSetName ,ISNULL( xmlcolumn1.value('(Query/DataSourceName/text())[1]','VARCHAR(250)'), xmlcolumn2.value('(r2016:Query/r2016:DataSourceName/text())[1]','VARCHAR(250)')) AS DataSourceName ,ISNULL( xmlcolumn1.value('(Query/CommandText/text())[1]','VARCHAR(2500)'), xmlcolumn2.value('(r2016:Query/r2016:CommandText/text())[1]','VARCHAR(2500)')) AS CommandText ,reportXML FROM ReportServer.dbo.Catalog C CROSS APPLY ( SELECT CONVERT(XML, CONVERT(VARBINARY(MAX), C.Content)) AS reportXML ) CATDATA OUTER APPLY reportXML.nodes('/Report/DataSets/DataSet') xmltable1 ( xmlcolumn1 ) OUTER APPLY reportXML.nodes('/r2016:Report/r2016:DataSets/r2016:DataSet') xmltable2 ( xmlcolumn2 ) WHERE C.Content is not null AND C.Type = 2 ORDER BY C.Name
It's hard to say which version is faster. Ignoring namespaces is slow, but so is querying twice.
Note other improvements:
- Use of
CROSS APPLYfor the conversion to avoid nesting. - Use of
/text()to increase performance when doing XQuery. - If you can change your column to actually be
xmland avoid the conversion that would be good.
As a side note, if you actually wanted that WHERE, the best way to do it would be to put it inside the .nodes (note the use of CROSS APPLY here, not OUTER APPLY.
CROSS APPLY reportXML.nodes('
/*Report/*:DataSets/*:DataSet
[*:Query/*:CommandText/text()[contains(., "2_")]
') xmltable ( xmlcolumn )
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论