如何创建一个 SQL 函数来解析多个 xml 格式的字符串?

How to create a SQL function to parse multiple xml formatted strings?

我正在尝试从 SQL 中的价格列中解析价格。我已经创建了一个函数来解析价格,但它仅在所有项目的“所有”价格字符串都相同时才有效。我现在意识到我需要一个更强大的函数来解析多个价格配置。

这是我的简单 SQL 查询

I.[NAME] AS 'ITEM NAME'
,DBO.parsePrice(I.PRICING) AS 'ITEM PRICE'
FROM ITEM I
order by 'ITEM NAME'

这是我的函数DBO.parsePrice

RETURNS decimal(8,2) AS 
BEGIN
    RETURN CAST(@in AS XML).value(N'(/prices[@type="Level"]/item[@level="A"])[1]/@price', N'DECIMAL(14,2)');
END

这是只有一个价格水平的 XML 字符串。

<prices type="Level"><item level="A" price="10.00" /></prices>

该函数将从上面的字符串中输出 10.00。

这是另一个最多有 6 个不同价格水平的例子

<prices type="Level"><item level="A" price="10.00" /><item level="B" price="11.00" /><item level="C" price="12.00" /><item level="D" price="13.00" /><item level="E" price="14.00" /><item level="F" price="15.00" /></prices>

我的函数仅用于解析价格水平 (A)。为了让事情变得更加复杂,价格字符串还可以配置不同的订单类型而不是价格水平。

<prices type="OrderType"><item level="DineIn" price="7" /><item level="TakeOut" price="8.00" /><item level="Delivery" price="" /><item level="Sale" price="" /></prices>

新功能需要处理所有这些不同的情况。理想情况下,我希望每个价格区间都有单独的列,而不是用逗号分隔价格的字段。

如有任何帮助,我们将不胜感激

此致,

JC

上面的SQL查询只是发布问题的一个例子。我的实际查询是使用左连接从其他表中提取数据。

SELECT
I.[NAME] AS 'ITEM NAME'
,DBO.parsePrice(I.PRICING) AS 'ITEM PRICE'
,ML.NAME AS 'MODIFIER LIST NAME'
,MG.NAME AS 'MODIFIER GROUP NAME'
,M.NAME AS 'MODIFIER NAME'
,M.UPCHARGE_EXPRESSION AS UPCHARGE
FROM ITEM I
LEFT JOIN [dbo].[ITEM_MODIFIER] IM ON IM.ITEM_RECORD_KEY = I.RECORD_KEY
LEFT JOIN [dbo].[MODIFIER_LIST] ML ON ML.RECORD_KEY = IM.MODIFIER_LIST_RECORD_KEY
LEFT JOIN [dbo].[MODIFIER_GROUP] MG ON MG.MODIFIER_LIST_RECORD_KEY = ML.RECORD_KEY
LEFT JOIN [dbo].[MODIFIER] M ON M.MODIFIER_GROUP_RECORD_KEY = MG.RECORD_KEY
order by 'ITEM NAME'

您可以创建一个 TVF 返回(水平,价格)

create function DBO.parsePrice(@x xml)
returns table
return
select p.n.value('@level', 'nvarchar(100)') level, p.n.value('@price', 'decimal(9,2)') price
from @x.nodes('prices/item') p(n)

并在查询中使用它

SELECT
I.[NAME] AS 'ITEM NAME'
, p.*
,ML.NAME AS 'MODIFIER LIST NAME'
,MG.NAME AS 'MODIFIER GROUP NAME'
,M.NAME AS 'MODIFIER NAME'
,M.UPCHARGE_EXPRESSION AS UPCHARGE
FROM ITEM I
LEFT JOIN [dbo].[ITEM_MODIFIER] IM ON IM.ITEM_RECORD_KEY = I.RECORD_KEY
LEFT JOIN [dbo].[MODIFIER_LIST] ML ON ML.RECORD_KEY = IM.MODIFIER_LIST_RECORD_KEY
LEFT JOIN [dbo].[MODIFIER_GROUP] MG ON MG.MODIFIER_LIST_RECORD_KEY = ML.RECORD_KEY
LEFT JOIN [dbo].[MODIFIER] M ON M.MODIFIER_GROUP_RECORD_KEY = MG.RECORD_KEY
CROSS APPLY DBO.parsePrice(I.PRICING) p
order by 'ITEM NAME', p.Level

编辑

按照 OP 的评论,这是 TVF 的一个版本,其中 returns 0 表示 @price 不是数字

create function DBO.parsePrice(@x xml)
returns table
return
select level, coalesce(try_cast(price as decimal(9,2)),0) price
from (
   select p.n.value('@level', 'varchar(100)') level, p.n.value('@price', 'nvarchar(50)') price
   from @x.nodes('prices/item') p(n)
) t

我已将 Serg 解决方案标记为正确解决方案,尽管我必须进行一些更改才能生成我们正在寻找的结果而不会出现数据类型转换错误。这是我正在使用的完整解决方案。

SQL 查询

SELECT
I.[NAME] AS 'ITEM NAME'
,ic.NAME AS 'ITEM CATEGORY'
,p.*
,ML.NAME AS 'MODIFIER LIST NAME'
,MG.NAME AS 'MODIFIER GROUP NAME'
,M.NAME AS 'MODIFIER NAME'
,M.UPCHARGE_EXPRESSION AS UPCHARGE
FROM ITEM I
LEFT JOIN [dbo].[ITEM_MODIFIER] IM ON IM.ITEM_RECORD_KEY = I.RECORD_KEY
LEFT JOIN [DBO].[ITEM_CATEGORY] IC ON IC.RECORD_KEY = I.ITEM_CATEGORY_RECORD_KEY
LEFT JOIN [dbo].[MODIFIER_LIST] ML ON ML.RECORD_KEY = IM.MODIFIER_LIST_RECORD_KEY
LEFT JOIN [dbo].[MODIFIER_GROUP] MG ON MG.MODIFIER_LIST_RECORD_KEY = ML.RECORD_KEY
LEFT JOIN [dbo].[MODIFIER] M ON M.MODIFIER_GROUP_RECORD_KEY = MG.RECORD_KEY
CROSS APPLY DBO.parsePrice(I.PRICING) p

/*where ml.name is not null*/
order by 'ITEM CATEGORY', 'ITEM NAME', P."Price TYPE", P."Price LEVEL"

Tabled_Valued函数

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE function [dbo].[parsePrice](@x xml)
returns table
return
select "PRICE TYPE", "PRICE LEVEL", coalesce(try_cast("Item Price" as decimal(9,2)),0) "ITEM PRICE"
from (
   select t.n.value('@type', 'varchar(100)') "PRICE TYPE", p.n.value('@level', 'varchar(100)') "PRICE LEVEL", p.n.value('@price', 'nvarchar(50)') "ITEM PRICE"
   from @x.nodes('//prices') t(n), @x.nodes('prices/item') p(n)
) t