我有一个具有这种结构的 XML 响应,但有大约 150 个不同的节点:
<?xml version="1.0" encoding="UTF-8"?>
<Orders>
<Order>
<OrderID>334</OrderID>
<AmountPaid currencyID="EUR">17.10</AmountPaid>
<UserID>marc58</UserID>
<ShippingAddress>
<Name>Marc Juppé</Name>
<Address>Rue</Address>
<City>Paris</City>
<StateOrProvince></StateOrProvince>
<Country>FR</Country>
<Phone>333333333</Phone>
<PostalCode>22222</PostalCode>
</ShippingAddress>
<ShippingCosts>4.50</ShippingCosts>
<Items>
<Item>
<Details>
<ItemID>3664</ItemID>
<Store>47</Store>
<Title>MCPU DDA010</Title>
<SKU>mmx</SKU>
</Details>
<Quantity>1</Quantity>
<Price currencyID="EUR">6.2</Price>
</Item>
<Item>
<Details>
<ItemID>3665</ItemID>
<Store>45</Store>
<Title>MCPU DFZ42</Title>
<SKU>mmy</SKU>
</Details>
<Quantity>2</Quantity>
<Price currencyID="EUR">3.2</Price>
</Item>
</Items>
</Order>
</Orders>
Run Code Online (Sandbox Code Playgroud)
我需要将此信息存储到 3 个不同的表中,对于Item
表,我需要为每个不同的表创建记录<Item>
,但还要插入订单节点的详细信息;像这样:
|ItemID|Store|Title |SKU|Quantity|Price|OrderID|AmountPaid|UserID|ShippingCost|
|3664 | 47|DDA010|mmx| 1| 6.2| 334| 17.10|marc58| 4.50|
|3665 | 45|DFZ42 |mmy| 2| 3.2| 334| 17.10|marc58| 4.50|
Run Code Online (Sandbox Code Playgroud)
为了在不同的表中“自动”写入所需的信息,我在社区的大力帮助下构建了这个查询:
Set @T1='Orders'
Set @F1='OrderID'
Set @V=''
SELECT
@C= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',@C)=0, CONCAT( ISNULL(@C + ',','') , QUOTENAME(T.X.value('local-name(.)', 'nvarchar(100)'))), @C),
@D= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',@CP)=0, CONCAT( ISNULL(@D + ',N','') , '''', T.X.value(N'text()[1]', 'nvarchar(max)'),''''), @D),
@U= IIF (CHARINDEX('['+T.X.value('local-name(.)', 'nvarchar(100)')+']',@CP)=0, CONCAT( ISNULL(@U + ',','') , QUOTENAME( T.X.value('local-name(.)', 'nvarchar(100)')) ,'=', '''',T.X.value(N'text()[1]', 'nvarchar(max)'),''''), @U),
@V= IIF(T.X.value('local-name(.)', 'nvarchar(100)') =@F2, T.X.value('text()[1]', 'nvarchar(100)'), @V),
FROM @XML.nodes('//*[count(child::*) = 0]') AS T(X)
WHERE T.X.value(N'local-name(.)', 'nvarchar(500)')
IN (select name from db1.sys.columns where [object_id]=OBJECT_ID(@T1)and is_identity=0)
SELECT @C = STUFF(@C, 1, 1, '');
SELECT @D = STUFF(@D, 1, 1, '');
SELECT @U = STUFF(@U, 1, 1, '');
SET @S=N'IF NOT EXISTS (SELECT 1 FROM '+@T1+' WHERE '+@F1+' = '''+@V+''')
INSERT INTO '+@T1+' ('+@C+') VALUES ('+@D+''')
ELSE UPDATE '+@T1+' SET '+@U+''' WHERE '+@F2+'='''+@V+''''
Print @S
EXEC sp_executesql .....
Set @T1="Users"
......
Run Code Online (Sandbox Code Playgroud)
好的,最好在这里使用循环
这个查询虽然肯定可以改进和优化,但到目前为止一直运行良好,因为只有 1 个 Item Node,但是现在,随着更多的 Item Node,它只返回第一个。
我试图修改 FROM 子句试图引用项目集合,但没有成功,但我认为即使我成功迭代项目节点,我也不知道如何获取订单节点的详细信息,即父节点项目节点...
你能提出一个解决方案吗?
谢谢
坏消息:脚本中的local-name(.)
plus@XML.nodes('//*[count(child::*) = 0]')
方法完全扁平化了 XML 文档,如果文档中有多个 X-per-Y 结构,则需要重新设计。如果需要动态 SQL,请提供示例以更轻松地测试该方面。
另一种方法可能是手动构建您需要的查询。如果您的基本问题是包含父信息,那么您可以修改下面的演示 SELECT 查询。
(NB Mikael Eriksson 的回答有一个改进的查询。请参考。)
关键思想:
.
是上下文节点,并..
为您提供父节点
.nodes
是属于查询的 FROM 部分的另一种 XML 函数,通常与 CROSS APPLY 一起出现。它为每个匹配返回一个指针,并且允许处理多行。在这里阅读更多。
.query().value
是让 SQL Server 知道 value 方法只有一条数据可以使用的几种方法之一(修复“需要单例”错误)
DECLARE @XML xml =
'<Orders>
<Order>
<OrderID>334</OrderID>
<AmountPaid currencyID="EUR">17.10</AmountPaid>
<UserID>marc58</UserID>
<ShippingAddress>
<Name>Marc Juppé</Name>
<Address>Rue</Address>
<City>Paris</City>
<StateOrProvince></StateOrProvince>
<Country>FR</Country>
<Phone>333333333</Phone>
<PostalCode>22222</PostalCode>
</ShippingAddress>
<ShippingCosts>4.50</ShippingCosts>
<Items>
<Item>
<Details>
<ItemID>3664</ItemID>
<Store>47</Store>
<Title>MCPU DDA010</Title>
<SKU>mmx</SKU>
</Details>
<Quantity>1</Quantity>
<Price currencyID="EUR">6.2</Price>
</Item>
<Item>
<Details>
<ItemID>3665</ItemID>
<Store>45</Store>
<Title>MCPU DFZ42</Title>
<SKU>mmy</SKU>
</Details>
<Quantity>2</Quantity>
<Price currencyID="EUR">3.2</Price>
</Item>
</Items>
</Order>
</Orders>'
SELECT
x.value('./ItemID[1]','int') AS ItemID,
x.value('./Store[1]','int') AS Store,
x.value('./Title[1]','nvarchar(100)') AS Title,
x.value('./SKU[1]','nvarchar(100)') AS SKU,
x.value('../Quantity[1]','int') AS Qty,
x.value('../Price[1]','decimal(11,2)') AS Price,
x.query('//OrderID[1]').value('.','int') AS OrderID,
x.query('//AmountPaid[1]').value('.','decimal(11,2)') AS AmountPaid,
x.query('//UserID[1]').value('.','nvarchar(100)') AS UserID,
x.query('//ShippingCosts[1]').value('.','decimal(11,2)') AS ShippingCosts
FROM @XML.nodes('//Item/Details') i(x)
Run Code Online (Sandbox Code Playgroud)
您从 Forrest 得到的查询可以稍微改进一下。
在 SQL Server 的 xQuery 中使用父轴几乎总是一个非常糟糕的主意。您可以通过Orders/Order
先切碎 on ,然后使用交叉应用来切碎 on 来避免这种情况Items/Item
。
使用也不query('').value('.')
是一个好主意。value()
最好使用 来确保您只从函数中获取一个值[1]
。
为了提高性能,一件额外的事情是text()
在value()
.
select I.X.value('(Details/ItemID/text())[1]', 'int') as ItemID,
I.X.value('(Details/Store/text())[1]', 'int') as Store,
I.X.value('(Details/Title/text())[1]', 'nvarchar(100)') as Title,
I.X.value('(Details/SKU/text())[1]', 'nvarchar(100)') as SKU,
I.X.value('(Quantity/text())[1]', 'int') as Quantity,
I.X.value('(Price/text())[1]', 'decimal(11,2)') as Quantity,
O.X.value('(OrderID/text())[1]', 'int') as OrderID,
O.X.value('(AmountPaid/text())[1]', 'decimal(11,2)') as AmountPaid,
O.X.value('(UserID/text())[1]', 'nvarchar(100)') as UserID,
O.X.value('(ShippingCosts/text())[1]', 'decimal(11,2)') as ShippingCosts
from @XML.nodes('/Orders/Order') as O(X)
cross apply O.X.nodes('Items/Item') as I(X);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
8391 次 |
最近记录: |