SQL XML提取变量属性名称和值
即使在读了一天stack overflow之后,我也无法完全理解这一点 我有一个带有XML列的SQL表。根标记中可以有变量属性名和最多5个数量,我希望将名称和数据与其他信息一起放入表中 我已经拼凑了一个解决方案,这个解决方案就足够了,但我真的不喜欢它,因为我使用row_number生成字段号,然后将其连接回来,所以我依靠SQL以正确的顺序提取属性名,我不确定这是否可靠?它当然没有感觉到 我应该如何以更稳健的方式进行这项工作 带有数据的示例代码SQL XML提取变量属性名称和值,sql,xml,Sql,Xml,即使在读了一天stack overflow之后,我也无法完全理解这一点 我有一个带有XML列的SQL表。根标记中可以有变量属性名和最多5个数量,我希望将名称和数据与其他信息一起放入表中 我已经拼凑了一个解决方案,这个解决方案就足够了,但我真的不喜欢它,因为我使用row_number生成字段号,然后将其连接回来,所以我依靠SQL以正确的顺序提取属性名,我不确定这是否可靠?它当然没有感觉到 我应该如何以更稳健的方式进行这项工作 带有数据的示例代码 ;with xmldata as ( SEL
;with xmldata as (
SELECT 1 as id,
CAST ('<CHG client="c;EN" work_order="c;130102"> <COL NAM="description" TYP="c"> <OLD>Electricity control on CONST01-CCU</OLD> <NEW>Electricity control on CONST01-CCU93</NEW>
</COL> <COL NAM="last_update" TYP="d"> <OLD>2021-03-01 09:43:40</OLD> <NEW>2021-03-01 09:43:40</NEW> </COL></CHG>' AS XML) xml_data
UNION ALL
SELECT 2 as ID,
CAST( '<CHG menu_id="c;40906" user_id="c;" role_id="c;SYSTEM"><COL NAM="bflag" TYP="i"><NEW>8</NEW></COL><COL NAM="last_update" TYP="d"><NEW>2021-03-01 11:52:40</NEW>
</COL><COL NAM="menu_id" TYP="c"><NEW>40906</NEW></COL><COL NAM="role_id" TYP="c"><NEW>SYSTEM</NEW></COL><COL NAM="tree_type" TYP="i">
<NEW>1</NEW></COL><COL NAM="user_id" TYP="c"><NEW></NEW></COL><COL NAM="user_stamp" TYP="c"><NEW>TEST1</NEW></COL> </CHG> ' AS XML) as xml_data)
,
cols as (SELECT id, attrs.z.value('local-name(.)[1]', 'nvarchar(max)') as attr,
ROW_NUMBER() OVER(PARTITION BY id ORDER BY id ) AS rowno
FROM xmldata d
CROSS APPLY d.xml_data.nodes('/CHG/@*') as attrs(z)
)
SELECT d.id,
cols1.attr key_1_name,
right( change.x.value('(@*)[1]','varchar(255)'), len(change.x.value('(@*)[1]','varchar(255)')) - charindex(';', change.x.value('(@*)[1]','varchar(255)'))) as key_1,
cols2.attr key_2_name,
right( change.x.value('(@*)[2]','varchar(255)'), len(change.x.value('(@*)[2]','varchar(255)')) - charindex(';', change.x.value('(@*)[2]','varchar(255)'))) as key_2,
cols3.attr key_3_name,
right( change.x.value('(@*)[3]','varchar(255)'), len(change.x.value('(@*)[3]','varchar(255)')) - charindex(';', change.x.value('(@*)[3]','varchar(255)'))) as key_3,
cols4.attr key_4_name,
right( change.x.value('(@*)[4]','varchar(255)'), len(change.x.value('(@*)[4]','varchar(255)')) - charindex(';', change.x.value('(@*)[4]','varchar(255)'))) as key_4,
cols5.attr key_5_name,
right( change.x.value('(@*)[5]','varchar(255)'), len(change.x.value('(@*)[5]','varchar(255)')) - charindex(';', change.x.value('(@*)[5]','varchar(255)'))) as key_5,
col.y.value('(@NAM)[1]','varchar(255)') as column_name,
col.y.value('(OLD)[1]','varchar(255)') as old_value,
col.y.value('(NEW)[1]','varchar(255)') as new_value
FROM xmldata d
OUTER APPLY d.xml_data.nodes('/CHG') as change(x)
OUTER APPLY d.xml_data.nodes('/CHG/COL') as col(y)
LEFT JOIN cols cols1
ON d.id = cols1.id AND cols1.rowno =1
LEFT JOIN cols cols2
ON d.id = cols2.id AND cols2.rowno =2
LEFT JOIN cols cols3
ON d.id = cols3.id AND cols3.rowno =3
LEFT JOIN cols cols4
ON d.id = cols4.id AND cols4.rowno =4
LEFT JOIN cols cols5
ON d.id = cols5.id AND cols5.rowno =5
请尝试以下解决方案 层次化XML通过链式交叉应用子句和适当的.nodes方法调用本身非常好地支持隐含关系 所以切碎很容易 SQL 1
在提问时,您需要提供一个最小的可复制示例。请参考以下链接:请提供以下内容:1 DDL和示例数据填充,即创建表和插入T-SQL语句。2您需要做什么,即逻辑和代码尝试在T-SQL中实现它。3期望输出,基于上述1中的样本数据。4您的SQL Server版本选择@@version;嗯,数据集都是自包含的,是基于CTE的查询,所以我认为1是没有意义的,2和3我认为是在我的解释中给出的,加上运行代码可以获得期望的结果,我正在寻找一种更健壮的方法来实现解决方案。这个版本也很有意义,因为我相信CTE和XML数据类型从2005年就已经存在了?谢谢,我需要的语法是:change.x.value'local-name@*[1],'VARCHAR30'@AdamDavies,很高兴听到建议的解决方案适合您。请投票:
-- DDL and sample data population, start
DECLARE @tbl TABLE (id INT IDENTITY PRIMARY KEY, xml_data XML);
INSERT INTO @tbl (xml_data) VALUES
(N'<CHG client="c;EN" work_order="c;130102">
<COL NAM="description" TYP="c">
<OLD>Electricity control on CONST01-CCU</OLD>
<NEW>Electricity control on CONST01-CCU93</NEW>
</COL>
<COL NAM="last_update" TYP="d">
<OLD>2021-03-01 09:43:40</OLD>
<NEW>2021-03-01 09:43:40</NEW>
</COL>
</CHG>'),
(N'<CHG menu_id="c;40906" user_id="c;" role_id="c;SYSTEM">
<COL NAM="bflag" TYP="i">
<NEW>8</NEW>
</COL>
<COL NAM="last_update" TYP="d">
<NEW>2021-03-01 11:52:40</NEW>
</COL>
<COL NAM="menu_id" TYP="c">
<NEW>40906</NEW>
</COL>
<COL NAM="role_id" TYP="c">
<NEW>SYSTEM</NEW>
</COL>
<COL NAM="tree_type" TYP="i">
<NEW>1</NEW>
</COL>
<COL NAM="user_id" TYP="c">
<NEW></NEW>
</COL>
<COL NAM="user_stamp" TYP="c">
<NEW>TEST1</NEW>
</COL>
</CHG>');
-- DDL and sample data population, end
DECLARE @prefix CHAR(2) = 'c;'
, @tilde CHAR(1) = '~'
, @separator CHAR(1) = ';';
SELECT id
, a.value('local-name((@*)[1])', 'VARCHAR(30)') AS key1
, REPLACE(a.value('(@*)[1]', 'VARCHAR(30)'), @prefix, '') AS value1
, a.value('local-name((@*)[2])', 'VARCHAR(30)') AS key2
, REPLACE(a.value('(@*)[2]', 'VARCHAR(30)'), @prefix, '') AS value2
, a.value('local-name((@*)[3])', 'VARCHAR(30)') AS key3
, REPLACE(a.value('(@*)[3]', 'VARCHAR(30)'), @prefix, '') AS value3
, a.value('local-name((@*)[4])', 'VARCHAR(30)') AS key4
, REPLACE(a.value('(@*)[4]', 'VARCHAR(30)'), @prefix, '') AS value4
, a.value('local-name((@*)[5])', 'VARCHAR(30)') AS key5
, REPLACE(a.value('(@*)[5]', 'VARCHAR(30)'), @prefix, '') AS value5
, y.value('@NAM', 'varchar(255)') AS column_name
, y.value('(OLD/text())[1]', 'varchar(255)') AS old_value
, y.value('(NEW/text())[1]', 'varchar(255)') AS new_value
FROM @tbl
CROSS APPLY xml_data.nodes('/CHG') AS t1(a)
CROSS APPLY t1.a.nodes('COL') AS t2(y);
SELECT id
, a.value('local-name((@*)[1])', 'VARCHAR(30)') AS key1
, REPLACE(a.value('(@*)[1]', 'VARCHAR(30)'), @prefix, '') AS value1
, (
SELECT LEFT(s.value, LEN(s.value) - 1)
FROM STRING_SPLIT(a.value('(@*)[1]', 'VARCHAR(30)') + @tilde, @separator) AS s
WHERE s.value LIKE '%' + @tilde
) AS value1
, a.value('local-name((@*)[2])', 'VARCHAR(30)') AS key2
, REPLACE(a.value('(@*)[2]', 'VARCHAR(30)'), @prefix, '') AS value2
, a.value('local-name((@*)[3])', 'VARCHAR(30)') AS key3
, REPLACE(a.value('(@*)[3]', 'VARCHAR(30)'), @prefix, '') AS value3
, a.value('local-name((@*)[4])', 'VARCHAR(30)') AS key4
, REPLACE(a.value('(@*)[4]', 'VARCHAR(30)'), @prefix, '') AS value4
, a.value('local-name((@*)[5])', 'VARCHAR(30)') AS key5
, REPLACE(a.value('(@*)[5]', 'VARCHAR(30)'), @prefix, '') AS value5
, y.value('@NAM', 'varchar(255)') AS column_name
, y.value('(OLD/text())[1]', 'varchar(255)') AS old_value
, y.value('(NEW/text())[1]', 'varchar(255)') AS new_value
FROM @tbl
CROSS APPLY xml_data.nodes('/CHG') AS t1(a)
CROSS APPLY t1.a.nodes('COL') AS t2(y);