从同一/另一个表中的另一列向XML列添加属性

时间:2013-08-06 21:46:58

标签: tsql sql-server-2005 sqlxml

这是我的情景:

--ORDER table
OrderID OrderCode DateShipped    ShipmentXML
1       ABC       08/06/2013     <Order><Item CustomerName="BF" City="Philadelphia" State="PA"></Item></Order>
2       XYZ       08/05/2013     <Order><Item CustomerName="TJ" City="Richmond" State="VA"></Item></Order>

在此过程中的某个时刻,我将了解这些订单的相应TrackingNumber。跟踪号可在另一个表中找到,如下所示:

 --TRACKING table
 TrackingID    OrderCode   TrackingNumber    
 98            ABC         1Z1               
 99            XYZ         1Z2

我期待的输出如下:

   OrderID OrderCode     ShipmentXML
   1       ABC           <Order><Item CustomerName="BF" City="Philadelphia" State="PA" DateShipped="08/06/2013" TrackingNumber="1Z1"></Item></Order>
   2       XYZ           <Order><Item CustomerName="TJ" City="Richmond" State="VA" DateShipped="08/05/2013" TrackingNumber="1Z2"></Item></Order>`

正如您所看到的,我正在尝试为每个TrackingNumber获取DateShippedOrderCode,并将它们作为属性。意图是SELECT,而不是UPDATE。

我见过的所有示例都演示了如何使用Constant值或变量更新XML。我找不到一个用JOIN演示XML更新的人。请帮助解决这个问题。

更新:

通过“选择不更新”,我的意思是永久表没有更新;临时表的更新完全没问题,正如Mikael在第一个答案下面评论的那样。

3 个答案:

答案 0 :(得分:3)

Prevous答案很好,但您必须明确指定列并将它们转换为varchar,这对未来的支持不利(如果您向ShipmentXML添加属性,则必须修改查询)。
相反,你可以使用XQuery:

select
    O.OrderID, O.OrderCode,
    (
        select
            (select O.DateShipped, T.TrackingNumber for xml raw('Item'), type),
            O.ShipmentXML.query('Order/*')
        for xml path(''), type
    ).query('<Order><Item>{for $i in Item/@* return $i}</Item></Order>')
from [ORDER] as O
    left outer join [TRACKING] as T on T.OrderCode = O.OrderCode

甚至是这样:

select
    O.OrderID, O.OrderCode,
    O.ShipmentXML.query('
            element Order {
                element Item {
                    attribute DateShipped {sql:column("O.DateShipped")},
                    attribute TrackingNumber {sql:column("T.TrackingNumber")},
                    for $i in Order/Item/@* return $i
                }
            }')
from [ORDER] as O
    left outer join [TRACKING] as T on T.OrderCode = O.OrderCode

请参阅sqlfiddle示例

答案 1 :(得分:3)

使用临时表将属性添加到XML的版本。

select OrderID,
       OrderCode,
       DateShipped,
       ShipmentXML
into #Order
from [Order]

update #Order
set ShipmentXML.modify
  ('insert attribute DateShipped {sql:column("DateShipped")} 
    into (/Order/Item)[1]')

update O
set ShipmentXML.modify
  ('insert attribute TrackingNumber {sql:column("T.TrackingNumber")} 
    into (/Order/Item)[1]')
from #Order as O
  inner join Tracking as T
    on O.OrderCode = T.OrderCode

select OrderID,
       OrderCode,
       ShipmentXML
from #Order

drop table #Order

答案 2 :(得分:1)

我知道允许对xml类型列中的数据进行部分修改的唯一方法是使用modify方法,但正如documentation中所述

  

xml数据类型的modify()方法只能在SET中使用   UPDATE语句的子句。

由于不需要UPDATE,因此作为一种解决方法,我会看到碎片并手动重新组合为:

select
    o.OrderID,
    o.OrderCode,
    (
        cast((select
            t.c.value('@CustomerName', 'varchar(50)') as '@CustomerName',
            t.c.value('@City', 'varchar(50)') as '@City',
            t.c.value('@State', 'varchar(50)') as '@State',
            o.DateShipped as '@DateShipped',
            tr.TrackingNumber as '@TrackingNumber'
        for xml path('Item'), root('Order')) as xml)
    ) as ShipmentXML
from
    [ORDER] o
    join [TRACKING] tr on tr.OrderCode = o.OrderCode
    cross apply o.ShipmentXML.nodes('Order/Item') t(c)

您可能需要将格式应用于o.DateShipped