Sql server 如何分解一般定义的relate XML(没有不同的元素名称)
第三方将提供我需要分解为SQLServer关系格式的XML。XML在几个方面是不寻常的 为了更加灵活,XML提供了一个带有列名的节。随后将提供实际数据,但不提供不同的元素名称,我可能需要根据顺序映射列名 提供实际数据的行部分没有标题属性或其他方式来关联行中的一组字段 以下是数据的简化版本:Sql server 如何分解一般定义的relate XML(没有不同的元素名称),sql-server,xml,Sql Server,Xml,第三方将提供我需要分解为SQLServer关系格式的XML。XML在几个方面是不寻常的 为了更加灵活,XML提供了一个带有列名的节。随后将提供实际数据,但不提供不同的元素名称,我可能需要根据顺序映射列名 提供实际数据的行部分没有标题属性或其他方式来关联行中的一组字段 以下是数据的简化版本: <ReportData> <ColumnName>SOLD_DATE</ColumnName> <ColumnName>STORE_NUMBER
<ReportData>
<ColumnName>SOLD_DATE</ColumnName>
<ColumnName>STORE_NUMBER</ColumnName>
<ColumnName>PHONE_NUMBER</ColumnName>
<ColumnName>FAX_NUMBER</ColumnName>
<Row>
<Col>03/31/2016</Col>
<Col>1234</Col>
<Col>(425) 673-7065</Col>
<Col>(425) 278-4974</Col>
</Row>
<Row>
<Col>05/05/2016</Col>
<Col>3456</Col>
<Col>(425) 555-7065</Col>
<Col>(425) 444-4974</Col>
</Row>
</ReportData>
谢谢你的建议。我最初考虑使用带有边缘表的OpenXML来利用父/同级节点值,但似乎应该有更好的方法。考虑查询节点,然后按节点索引选择:
如果对象_ID'tempdb..demo,'U'不是空的drop table demo;
创建表演示数据xml
插入到演示数据中
价值观
售出日期
商店号码
电话号码
传真号码
03/31/2016
1234
425 673-7065
425 278-4974
05/05/2016
3456
425 555-7065
425 444-4974
';
选择
售出日期=物品.价值'Col[1],'varchar50',
Store_Number=item.value'Col[2],'varchar50',
电话号码=项目值'Col[3],'varchar50',
传真号=项目值'Col[4],'varchar50'
从演示
交叉应用
data.nodes'/ReportData/Row'作为dtitem;
-售出日期商店电话传真号码
- 03/31/2016 1234 425 673-7065 425 278-4974
- 05/05/2016 3456 425 555-7065 425 444-4974
也许没有一个优雅的解决方案。至少可以将原始数据转换为可读性更好的数据。这里有一个例子
declare @x xml=
'<ReportData>
<ColumnName>SOLD_DATE</ColumnName>
<ColumnName>STORE_NUMBER</ColumnName>
<ColumnName>PHONE_NUMBER</ColumnName>
<ColumnName>FAX_NUMBER</ColumnName>
<Row>
<Col>03/31/2016</Col>
<Col>1234</Col>
<Col>(425) 673-7065</Col>
<Col>(425) 278-4974</Col>
</Row>
<Row>
<Col>05/05/2016</Col>
<Col>3456</Col>
<Col>(425) 555-7065</Col>
<Col>(425) 444-4974</Col>
</Row>
</ReportData>'
declare @data xml
;with nm as (--ColumnNames
select t.v.value('ColumnName[1]','varchar(20)') c1,
t.v.value('ColumnName[2]','varchar(20)') c2,
t.v.value('ColumnName[3]','varchar(20)') c3,
t.v.value('ColumnName[4]','varchar(20)') c4
from @x.nodes('ReportData') t(v)
)
,ro as (--rows
select t.v.value('Col[1]','varchar(20)') r1,
t.v.value('Col[2]','varchar(20)') r2,
t.v.value('Col[3]','varchar(20)') r3,
t.v.value('Col[4]','varchar(20)') r4
from @x.nodes('ReportData/Row') t(v)
)
select @data= (select cast('<row '+nm.c1+'="'+ro.r1+'" '
+nm.c2+'="'+ro.r2+'" '
+nm.c3+'="'+ro.r3+'" '
+nm.c4+'="'+ro.r4+'" />' as xml)
from ro
cross apply nm
for xml path(''),root)
select @data d
生成的XML如下所示
<root>
<row SOLD_DATE="03/31/2016" STORE_NUMBER="1234" PHONE_NUMBER="(425) 673-7065" FAX_NUMBER="(425) 278-4974" />
<row SOLD_DATE="05/05/2016" STORE_NUMBER="3456" PHONE_NUMBER="(425) 555-7065" FAX_NUMBER="(425) 444-4974" />
</root>
如果需要这样的输出,则需要动态命名列。这是不可能的-除非您使用动态SQL。试试这个:
DECLARE @xml XML=
'<ReportData>
<ColumnName>SOLD_DATE</ColumnName>
<ColumnName>STORE_NUMBER</ColumnName>
<ColumnName>PHONE_NUMBER</ColumnName>
<ColumnName>FAX_NUMBER</ColumnName>
<Row>
<Col>03/31/2016</Col>
<Col>1234</Col>
<Col>(425) 673-7065</Col>
<Col>(425) 278-4974</Col>
</Row>
<Row>
<Col>05/05/2016</Col>
<Col>3456</Col>
<Col>(425) 555-7065</Col>
<Col>(425) 444-4974</Col>
</Row>
</ReportData>';
TMPRESLT表现在包含以下内容:
+-------+-------+----------------+-------+--------------+
| RowNr | ValNr | ColVal | ColNr | Caption |
+-------+-------+----------------+-------+--------------+
| 1 | 1 | 03/31/2016 | 1 | SOLD_DATE |
+-------+-------+----------------+-------+--------------+
| 1 | 2 | 1234 | 2 | STORE_NUMBER |
+-------+-------+----------------+-------+--------------+
| 1 | 3 | (425) 673-7065 | 3 | PHONE_NUMBER |
+-------+-------+----------------+-------+--------------+
| 1 | 4 | (425) 278-4974 | 4 | FAX_NUMBER |
+-------+-------+----------------+-------+--------------+
| 2 | 1 | 05/05/2016 | 1 | SOLD_DATE |
+-------+-------+----------------+-------+--------------+
| 2 | 2 | 3456 | 2 | STORE_NUMBER |
+-------+-------+----------------+-------+--------------+
| 2 | 3 | (425) 555-7065 | 3 | PHONE_NUMBER |
+-------+-------+----------------+-------+--------------+
| 2 | 4 | (425) 444-4974 | 4 | FAX_NUMBER |
+-------+-------+----------------+-------+--------------+
现在我们需要一个动态创建的PIVOT语句:
DECLARE @colNames NVARCHAR(MAX)=
(
STUFF(
(
SELECT DISTINCT ',' + Caption + ''
FROM #tmpResult
FOR XML PATH('')
),1,1,''
)
);
DECLARE @cmd NVARCHAR(MAX)=
'SELECT p.*
FROM
(
SELECT RowNr,ColVal,Caption FROM #tmpResult
) AS tbl
PIVOT
(
MAX(ColVal) FOR Caption IN(' + @colNames + ')
) AS p;';
EXEC(@cmd);
这就是结果:
+-------+----------------+----------------+------------+--------------+
| RowNr | FAX_NUMBER | PHONE_NUMBER | SOLD_DATE | STORE_NUMBER |
+-------+----------------+----------------+------------+--------------+
| 1 | (425) 278-4974 | (425) 673-7065 | 03/31/2016 | 1234 |
+-------+----------------+----------------+------------+--------------+
| 2 | (425) 444-4974 | (425) 555-7065 | 05/05/2016 | 3456 |
+-------+----------------+----------------+------------+--------------+
清理
DROP TABLE #tmpResult;
我喜欢创建更好的XML的方法,但使用XSLT可能更容易。OP在这里是数据的一个简化版本,让我想,原始XML可能有更多的列,甚至可能不是相同的数字。还有一个提示:如果数据可能包含禁止使用的字符,这将中断。这是可以解决的。我在这里发布了一个解决方案:这是一个非常简化的数据版本:让我觉得,这是不可能的,因为有固定位置和标题…而且,这是非常简单的遵循,但如果添加新的列,可能需要修改。哇-感谢大家的创意。Shnugo的解决方案具有动态处理任意数量列的优势。如果供应商添加新列,则该方法将在结果集中包含新列。这个解决方案正是我在考虑edge表时所考虑的。但这更好。使用.nodes方法实现这一点时,我缺少的部分没有意识到我可以使用select null调用row_number来获取行顺序,而不需要实际排序!这将在许多方面有所帮助。再次感谢。我明白了-你是对的,我不理解协议!
+-------+----------------+----------------+------------+--------------+
| RowNr | FAX_NUMBER | PHONE_NUMBER | SOLD_DATE | STORE_NUMBER |
+-------+----------------+----------------+------------+--------------+
| 1 | (425) 278-4974 | (425) 673-7065 | 03/31/2016 | 1234 |
+-------+----------------+----------------+------------+--------------+
| 2 | (425) 444-4974 | (425) 555-7065 | 05/05/2016 | 3456 |
+-------+----------------+----------------+------------+--------------+
DROP TABLE #tmpResult;