英文:
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
- 或者你可以使用多个
.nodes
WITH 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
.nodes
WITH 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 APPLY
for the conversion to avoid nesting. - Use of
/text()
to increase performance when doing XQuery. - If you can change your column to actually be
xml
and 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 )
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论