mssql将xml结构转换为表

时间:2015-02-21 18:34:30

标签: sql-server xml

我有一个用户配置文件存储在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作为其行值的列

类似的东西:

What i'm after

我已经设法以非常好的方式解决这个问题 首先将Xml转换为表格,左边连接并樱桃挑选每一列 毫无疑问,我会在请求新的配置文件字段时立即面对性能问题以及维护。

我在SqlFiddle上设置了一个关于我目前如何实现目标的例子 任何人都可以引导我朝着更好的方向前进吗? 或者我的XmlStructure让我没有选择?

我发现很多使用属性的例子,但这个对象都是节点值。

Sql Fiddle Example

2 个答案:

答案 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