سؤال

I have a SQL Server 2008 table with 50k rows, each with an XML fragment in a varchar column that looks like this:

<infoElems>
        <infoElem id="1" Name="somename" money="3399.3984939" />
</infoElems>

I need to select out the varchar column, select out the money attribute, change it to an actual money type (3399.40 in my example), and then put the whole fragment back.

Can anyone point how to get me through this? I think I need to create an XML index of some sort? Confused.

Thanks.

هل كانت مفيدة؟

المحلول

The datatypes involved make this ugly, especially if the XML in your columns varies other than the price. Here is something that will work in SQL 2008. If you have to update lots of rows you will have to use this with a CURSOR.

DECLARE @orig VARCHAR(20), @new money
DECLARE @origXml XML, @newXml VARCHAR(100)

// first you have to cast to xml
SELECT @origXml = CAST(myColunm AS XML) 
FROM dbo.Tbl1
WHERE ...

// then extract the value as a string
SET @orig = @origXml.value('(//infoElem/@money)[1]','varchar(20)') 

// then get the new value to the right percision - MONEY is accurate to the ten-thousandth hence the ROUND
SET @new = ROUND(CAST(@orig AS MONEY),2)  

SET @newXml = REPLACE(CAST(@origXml AS VARCHAR(100)),  
  'money="'+CAST(@orig AS VARCHAR)+'"',
  'money="'+CAST(@new AS VARCHAR)+'"') // then replace - this can be combined with the update 

UPDATE dbo.Tbl1 SET myColunm = @newXml // then update

Depending on your situation, it may be easier to just write an external script to do this. You could also look into using regex - see this post, but that could get really ugly.

نصائح أخرى

-- Table with xml fragment
declare @YourTable table(SomeID int identity, YourColumn varchar(max))

-- Add 2 rows of test data
insert into @YourTable values(
'<infoElems>
        <infoElem id="1" Name="somename" money="3399.3984939" />
</infoElems>')

insert into @YourTable values(
'<infoElems>
        <infoElem id="1" Name="somename" money="4399.3584939" />
</infoElems>')

-- Declare a table variable with a xml column
declare @TempTable table(SomeID int, YourColumn xml)

-- Copy rows that should be modified (ID and xml is enough)
insert into @TempTable
select SomeID, YourColumn
from @YourTable

--Modify the money attribute in TempTable
;with cte as
(
  select YourColumn.value('(infoElems/infoElem/@money)[1]', 'money') as MoneyCol,
         YourColumn
  from @TempTable
)
update cte set
  YourColumn.modify('replace value of (infoElems/infoElem/@money)[1] with sql:column("MoneyCol")')  

-- Write the changes back to the source table
update Y set
  Y.YourColumn = cast(T.YourColumn as varchar(max))
from @YourTable as Y
  inner join @TempTable as T
    on Y.SomeID = T.SomeID

-- Look at the result  
select *
from @YourTable  

Result:

SomeID  YourColumn
------  ---------------------------------------------------------------------------
1       <infoElems><infoElem id="1" Name="somename" money="3399.3985"/></infoElems>
2       <infoElems><infoElem id="1" Name="somename" money="4399.3585"/></infoElems>

Money in SQL Server has 4 decimals. If you want 2 decimals you should use this update statement instead.

--Modify the money attribute in TempTable
;with cte as
(
  select YourColumn.value('(infoElems/infoElem/@money)[1]', 'numeric(15,2)') as MoneyCol,
         YourColumn
  from @TempTable
)
update cte set
  YourColumn.modify('replace value of (infoElems/infoElem/@money)[1] with sql:column("MoneyCol")')  

Basically, you can do something like this:

SELECT
   SomeID,
   CAST(YourColumn AS XML).value('(/infoElems/infoElem/@money)[1]', 'money') AS 'MoneyValue'
FROM 
   dbo.YourTable
WHERE
   ....

to get a list of your rows and the money attribute from your XML.

It would be a lot easier if that column - containing XML only - would actually be of type XML.... you could save yourself the CAST(....AS XML)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top