4

我在 Orders 表中有一个名为 OrderXML 的 xml 列...表中
有一个像这样的 XML XPath...

/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail

那里 InternalOrderDetails 包含许多像这样的 InternalOrderDetail 节点......

<InternalOrderDetails>
  <InternalOrderDetail>
    <Item_Number>FBL11REFBK</Item_Number>
    <CountOfNumber>10</CountOfNumber>
    <PriceLevel>FREE</PriceLevel>
  </InternalOrderDetail>
  <InternalOrderDetail>
    <Item_Number>FCL13COTRGUID</Item_Number>
    <CountOfNumber>2</CountOfNumber>
    <PriceLevel>NONFREE</PriceLevel>
  </InternalOrderDetail>
</InternalOrderDetails>

如果节点的 Item_Number 包含 COTRGUID(如 '%COTRGUID')并且 PriceLevel=NONFREE,我的最终目标是修改 OrderXML 列中的 XML。如果满足该条件,我想将 PriceLevel 列更改为 FREE。

我在创建找到正确节点的 xpath 表达式(使用 OrderXML.value 或 OrderXML.exist 函数)和使用 OrderXML.modify 函数更新 XML 时都遇到了麻烦。

对于 where 子句,我尝试了以下方法:

WHERE OrderXML.value('(/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail/Item_Number/node())[1]','nvarchar(64)') like '%13COTRGUID'

这确实有效,但在我看来,我还需要在同一个 where 子句中包含我的第二个条件(PriceLevel=NONFREE),我不知道该怎么做。也许我可以为这样的第二个条件加上一个 AND ......

AND OrderXML.value('(/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail/PriceLevel/node())[1]','nvarchar(64)') = 'NONFREE'

但我担心它最终会像 OR 一样运行,因为它是一个 XML 查询。

一旦我得到正确的 WHERE 子句,我将使用这样的 SET 更新列:

UPDATE Orders SET orderXml.modify('replace value of (/Order/InternalInformation/InternalOrderBreakout/InternalOrderHeader/InternalOrderDetails/InternalOrderDetail/PriceLevel[1]/text())[1] with "NONFREE"')

但是,我在一些测试数据上运行了这个语句,并且没有更新的 XML 列(即使它说 zz 行受到影响)。

我一直在这几个小时无济于事。帮助表示赞赏。谢谢。

4

3 回答 3

4

如果在 Orders 表的每一行中您的条件不超过一个节点,则可以使用以下命令:

update orders set
    data.modify('
        replace value of 
        (
            /Order/InternalInformation/InternalOrderBreakout/
            InternalOrderHeader/InternalOrderDetails/
            InternalOrderDetail[
                Item_Number[contains(., "COTRGUID")] and
                PriceLevel="NONFREE"
            ]/PriceLevel/text()
        )[1]
        with "FREE"
    ');

sql fiddle demo

如果您可以在一行中有多个节点,那么有几种可能的解决方案,可悲的是,每一种都不是真正优雅的。

于 2013-08-18T07:41:48.877 回答
0

尝试这个 :

;with InternalOrderDetail as (SELECT  id,
       Tbl.Col.value('Item_Number[1]', 'varchar(40)') Item_Number,  
       Tbl.Col.value('CountOfNumber[1]', 'int') CountOfNumber,  
case 
when Tbl.Col.value('Item_Number[1]', 'varchar(40)') like '%COTRGUID'
      and Tbl.Col.value('PriceLevel[1]', 'varchar(40)')='NONFREE'
                   then 'FREE'
else
       Tbl.Col.value('PriceLevel[1]', 'varchar(40)')
end
PriceLevel

FROM   (select id ,orderxml from demo) 
as a cross apply orderxml.nodes('//InternalOrderDetail')
as  
tbl(col) ) ,
cte_data as(SELECT 
  ID,
'<InternalOrderDetails>'+(SELECT ITEM_NUMBER,COUNTOFNUMBER,PRICELEVEL
FROM InternalOrderDetail
where ID=Results.ID
FOR XML AUTO, ELEMENTS)+'</InternalOrderDetails>' as XML_data
FROM InternalOrderDetail Results
GROUP BY ID)
update demo set orderxml=cast(xml_data as xml)
from demo 
inner join cte_data on demo.id=cte_data.id
where cast(orderxml as varchar(2000))!=xml_data;

select * from demo;

SQL小提琴

我处理过以下案例:
1. 根据需要,两个 where 子句都有问题。
2. 它将在一个节点中更新所有<Item_Number>类似 '%COTRGUID' 和 <PriceLevel>= NONFREE 的内容,而不仅仅是第一个节点。

它可能需要对您的数据和表格进行细微更改。

于 2013-06-18T08:43:18.870 回答
0

这可能会让你摆脱困境。

将#HolderTable 替换为您的表的名称。

SELECT T2.myAlias.query('./../PriceLevel[1]').value('.' , 'varchar(64)') as MyXmlFragmentValue
FROM   #HolderTable
CROSS APPLY OrderXML.nodes('/InternalOrderDetails/InternalOrderDetail/Item_Number') as T2(myAlias) 


SELECT T2.myAlias.query('.') as MyXmlFragment
FROM   #HolderTable
CROSS APPLY OrderXML.nodes('/InternalOrderDetails/InternalOrderDetail/Item_Number') as T2(myAlias) 

编辑:

UPDATE 
   #HolderTable
SET 
   OrderXML.modify('replace value of (/InternalOrderDetails/InternalOrderDetail/PriceLevel/text())[1] with "MyNewValue"') 
WHERE  
   OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/PriceLevel)[1]', 'varchar(64)') = 'FREE' 

print @@ROWCOUNT

您的问题是上面的 [1]。为什么我把它放在那里?

这是下面列出的 URL 中的一句话。

请注意,被更新的目标最多只能是一个在路径表达式中通过在表达式末尾添加“[1]”明确指定的节点。

http://msdn.microsoft.com/en-us/library/ms190675.aspx

编辑。

我想我已经找到了你沮丧的根源。(没有修复,只是问题)。

请注意,第二个查询有效。

所以我认为 [1] 在某些情况下是说“只有 ~~search~~ 第一个节点”......而不是(正如你和我所希望的那样)......“使用第一个节点。 .找到匹配项之后”。

UPDATE 
   #HolderTable
SET 
   OrderXML.modify('replace value of (/InternalOrderDetails/InternalOrderDetail/PriceLevel/text())[1] with "MyNewValue001"') 
WHERE  
   OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/PriceLevel[text() = "NONFREE"])[1]', 'varchar(64)') = 'NONFREE' 
    /* and OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/Item_Number)[1]', 'varchar(64)') like '%COTRGUID' */



UPDATE 
   #HolderTable
SET 
   OrderXML.modify('replace value of (/InternalOrderDetails/InternalOrderDetail/PriceLevel/text())[1] with "MyNewValue002"') 
WHERE  
   OrderXML.value('(/InternalOrderDetails/InternalOrderDetail/PriceLevel[text() = "FREE"])[1]', 'varchar(64)') = 'FREE' 
于 2013-06-17T20:56:13.550 回答