我有一个用户配置文件存储在xml的sql数据库列中 我正在尝试构建一个平面表结构,以在报告中显示这些数据 我正在使用MSSQL 2012。
我的结构是动态的,并从一系列对象中序列化 在Xml中,我的结构如下所示:
<ArrayOfXmlField>
<XmlField>
<Name>FinancialYearEnd</Name>
<Value>11 February 2015</Value>
</XmlField>
<XmlField>
<Name>Amount</Name>
<Value>350</Value>
</XmlField>
<XmlField>
<Name>Capturer</Name>
<Value>Ted Mosby</Value>
</XmlField>
</ArrayOfXmlField>
结构可能包含也可能不包含所有这些字段 我希望结果表将每个XmlField.Name转换为一个XmlField.Value作为其行值的列
类似的东西:
我已经设法以非常好的方式解决这个问题 首先将Xml转换为表格,左边连接并樱桃挑选每一列 毫无疑问,我会在请求新的配置文件字段时立即面对性能问题以及维护。
我在SqlFiddle上设置了一个关于我目前如何实现目标的例子 任何人都可以引导我朝着更好的方向前进吗? 或者我的XmlStructure让我没有选择?
我发现很多使用属性的例子,但这个对象都是节点值。
答案 0 :(得分:1)
您无法通过XML构建动态结果集 - XPath查询的输入与SQL查询一样多。虽然你可以使用动态SQL(通过动态SQL解决不的问题),但这些方面的任何解决方案效率都很低,并且难以合并到查询中(你可以在存储中实现)程序,而不是其他地方)。
只要您的字段仍然存在,就不难从它们生成表格,代码少于链接的小提琴:
SELECT
ProfileID,
VarFields.value('(/ArrayOfXmlField/XmlField[Name="FinancialYearEnd"]/Value)[1]', 'nvarchar(500)') AS FinancialYearEnd,
VarFields.value('(/ArrayOfXmlField/XmlField[Name="Amount"]/Value)[1]', 'nvarchar(500)') AS Amount,
VarFields.value('(/ArrayOfXmlField/XmlField[Name="Capturer"]/Value)[1]', 'nvarchar(500)') AS Capturer
FROM [Profile]
如果您将表创建为函数,则不必在查询中重复此操作:
CREATE FUNCTION dbo.ProfileFields(@Fields XML) RETURNS TABLE AS
RETURN
SELECT
@Fields.value('(/ArrayOfXmlField/XmlField[Name="FinancialYearEnd"]/Value)[1]', 'nvarchar(500)') AS FinancialYearEnd,
@Fields.value('(/ArrayOfXmlField/XmlField[Name="Amount"]/Value)[1]', 'nvarchar(500)') AS Amount,
@Fields.value('(/ArrayOfXmlField/XmlField[Name="Capturer"]/Value)[1]', 'nvarchar(500)') AS Capturer
现在你可以使用
了SELECT ProfileID, Fields.FinancialYearEnd, Fields.Amount, Fields.Capturer
FROM [Profile] CROSS APPLY dbo.ProfileFields([Profile].VarFields) AS Fields
您可以将其放在视图中以便进一步重用。
当您想出一个新字段时,这只需要添加两行,但您仍然需要添加这些行。如果报表工具可以处理可变数量的字段,则可以将XML转换为名称/值表:
SELECT
ProfileID,
f.value('Name[1]', 'NVARCHAR(100)') AS FieldName,
f.value('Value[1]', 'NVARCHAR(500)') AS FieldValue
FROM [Profile]
CROSS APPLY VarFields.nodes('/ArrayOfXmlField/XmlField') AS Fields(f)
答案 1 :(得分:1)
您可以简化初始查询,只需转动数据:
with data as (
select
ProfileId,
vfields.value('(Name)[1]','varchar(500)') as FieldName,
vfields.value('(Value)[1]','varchar(500)') as FieldValue
from dbo.[Profile]
cross apply VarFields.nodes('/ArrayOfXmlField/XmlField') as t(vfields)
where VarFields is not null
)
select
ProfileId,
max(case when FieldName = 'FinancialYearEnd' then FieldValue else '' end) as FinancialYearEnd,
max(case when FieldName = 'Amount' then FieldValue else '' end) as Amount,
max(case when FieldName = 'Capturer' then FieldValue else '' end) as Capturer
from data
group by ProfileId
<强> sql fiddle demo 强>
或者只是xpath来查询您的数据:
select
ProfileId,
vfields.value('(XmlField[Name = "FinancialYearEnd"]/Value)[1]','varchar(500)') as FinancialYearEnd,
vfields.value('(XmlField[Name = "Amount"]/Value)[1]','varchar(500)') as Amount,
vfields.value('(XmlField[Name = "Capturer"]/Value)[1]','varchar(500)') as Capturer
from dbo.[Profile]
cross apply VarFields.nodes('/ArrayOfXmlField') as t(vfields)
<强> sql fiddle demo 强>