如何解决SQL-如何从表的XML字段中提取多个属性
我正在尝试在一个表上运行SQL查询,该表具有一个包含XML数据的字段,但是该XML包含多个值,这些值需要转换为一个字段。请注意,该字段是XML内容,但实际的字段类型设置为nvarchar(max),而不是xml。
编辑:版本为 SQL Server 2014 Express Edition
我有一个这样的表: [有市场的客户清单]
我想在同一行(以逗号分隔)中提取“ marketCode”值:
|CompanyCode|CompanyName|MarketCode,MarketCode,etc.|Phone|
示例的预期输出(请参见屏幕截图):
|ABC123|JOHN DEERE|AA,BB,CC,DD|555-123-000|
|DEF456|NEW HOLLLAND|AA,FF,GG,HH,KK|555-456-0000|
解决方法
样本数据
请下次提供文字而不是图片;-)
列Markets
被定义为nvarchar(max)
。数据以unicode(带有N
前缀)插入。
create table Company2
(
Code nvarchar(6),Name nvarchar(11),Markets nvarchar(max),Phone nvarchar(12)
);
insert into Company2 (Code,Name,Markets,Phone) values
(N'ABC123',N'JOHN DEERE',N'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<license>
<company companyCode="ABC123">
<markets>
<market marketCode="AA"/>
<market marketCode="BB"/>
<market marketCode="CC"/>
<market marketCode="DD"/>
</markets>
</company>
</license>',N'555-123-0000'),(N'DEF456',N'NEW HOLLAND',N'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<license>
<company companyCode="DEF456">
<markets>
<market marketCode="AA"/>
<market marketCode="FF"/>
<market marketCode="GG"/>
<market marketCode="HH"/>
<market marketCode="KK"/>
</markets>
</company>
</license>',N'555-456-0000');
解决方案
无法将Markets
直接投射到XML
,因为nvarchar(max)
编码与数据中的“ utf-8”冲突。我将转换移动到一个单独的公用表表达式(CTE,cte_convert
)中,从nvarchar(max)
到varchar(max)
到XML
。
下一个CTE(cte_parse
)现在可以使用c.MarketsXML.nodes()
从XML提取<market>
节点到新列m.Market
中。从该列中提取@marketCode
属性作为所需值。
然后使用带有for xml path('')
的子查询来连接值。
with cte_convert as
(
select c.Code,c.Name,convert(XML,convert(varchar(max),c.Markets)) as MarketsXML,c.Phone
from Company2 c
),cte_parse as
(
select c.Code,m.Market.value('@marketCode','nvarchar(10)') as MarketCode,c.Phone
from cte_convert c
outer apply c.MarketsXML.nodes('/license/company/markets/market') as m(Market)
)
select cp.Code,cp.Name,stuff(( select ',' + cp2.MarketCode as MC
from cte_parse cp2
where cp2.Code = cp.Code
for xml path(''),type).value('.','nvarchar(max)'),1,'') as MarketCodes,cp.Phone
from cte_parse cp
group by cp.Code,cp.Phone;
结果
Code Name MarketCodes Phone
------ ----------- -------------- ------------
ABC123 JOHN DEERE AA,BB,CC,DD 555-123-0000
DEF456 NEW HOLLAND AA,FF,GG,HH,KK 555-456-0000
此原始解决方案使用了string_agg()函数,该函数从SQL Server 2017开始可用。
样本数据
注释:Markets
列被定义为XML
以反映其内容。
create table Company
(
Code nvarchar(6),Markets XML,Phone nvarchar(12)
);
insert into Company (Code,Phone) values
('ABC123','JOHN DEERE','<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<license>
<company companyCode="ABC123">
<markets>
<market marketCode="AA"/>
<market marketCode="BB"/>
<market marketCode="CC"/>
<market marketCode="DD"/>
</markets>
</company>
</license>','555-123-0000'),('DEF456','NEW HOLLAND','<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<license>
<company companyCode="DEF456">
<markets>
<market marketCode="AA"/>
<market marketCode="FF"/>
<market marketCode="GG"/>
<market marketCode="HH"/>
<market marketCode="KK"/>
</markets>
</company>
</license>','555-456-0000');
解决方案
with cte_parse as
(
select c.Code,c.Phone
from Company c
outer apply c.Markets.nodes('/license/company/markets/market') as m(Market)
)
select cp.Code,string_agg(cp.MarketCode,',') as MarketCodes,cp.Phone;
,
declare @t table(CompanyMarkets nvarchar(max));
insert into @t(CompanyMarkets)
values(N'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<license>
<company companyCode="ABC123">
<markets>
<market marketCode="AA"/>
<market marketCode="BB"/>
<market marketCode="CC"/>
<market marketCode="DD"/>
</markets>
</company>
</license>');
select
--remove encoding ...
try_cast(replace(CompanyMarkets,'encoding="UTF-8"','') as xml),--... or transform any prolog to a weird&harmless processing instruction
try_cast(concat(case when CompanyMarkets like N'<?xml%' then '<?x ' end,CompanyMarkets) as xml)
from @t;
select *,try_cast(concat(case when CompanyMarkets like N'<?xml%' then '<?x ' end,CompanyMarkets) as xml).query('
for $i in (data(/license/company/markets/market/@marketCode)[1],(for $k in data(/license/company/markets/market/@marketCode)[position()>1] return concat(",",$k)))
return text {$i}
').value('.',--?? no spaces in marketCodes
replace(
try_cast(concat(case when CompanyMarkets like N'<?xml%' then '<?x ' end,CompanyMarkets) as xml).query('data(/license/company/markets/market/@marketCode)').value('.',' ','),stuff(
try_cast(concat(case when CompanyMarkets like N'<?xml%' then '<?x ' end,CompanyMarkets) as xml).query('
for $i in data(/license/company/markets/market/@marketCode) return text {concat(",$i)}
').value('.','')
from @t;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。