How to Update XML in SQL based on values in that XML

时光总嘲笑我的痴心妄想 提交于 2020-01-05 11:04:04

问题


I have to update XML of table based on some conditions of that XML. Sample XML:

<CountryValues>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>1</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>2</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>3</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>4</Month>
    <PlaceValue>10</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Australia</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>1</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Australia</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>1</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Australia</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>1</Month>
    <PlaceValue>4</PlaceValue>        
  </CountryRow>
 </CountryValues>

Each Country can have multiple Places. I have to group on the basis of Country and Places, then I have to update PlaceValues to null for PlaceValue = 0 except 0 which is immediately preceding PlaceValue > 1. Example in this sample, for Country = Brazil and PlaceName = 1, PlaceValue for Month1 to Month2 will be Null but Month3 will remain 0 as its preceding Month4 which is greate than 0.


回答1:


Basically, I see 2 ways of dealing with this. First - split xml to sql table/derived table, do your work and then combine into xml again.

declare @data xml = 
'<CountryValues>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>1</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>2</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>3</Month>
    <PlaceValue>0</PlaceValue>        
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>4</Month>
    <PlaceValue>10</PlaceValue>        
  </CountryRow>
 </CountryValues>'

;with cte as (
    select
        t.c.value('CountryName[1]', 'nvarchar(max)') as CountryName,
        t.c.value('PlaceName[1]', 'nvarchar(max)') as PlaceName,
        t.c.value('Month[1]', 'int') as [Month],
        t.c.value('PlaceValue[1]', 'int') as PlaceValue
    from @data.nodes('CountryValues/CountryRow') as t(c)
)
select
    c1.CountryName,
    c1.PlaceName,
    c1.[Month],
    case
        when c1.PlaceValue = 0 and isnull(c2.PlaceValue, 0) <= 1 then null
        else c1.PlaceValue
    end as PlaceValue
from cte as c1
    left outer join cte as c2 on c2.CountryName = c1.CountryName and c2.PlaceName = c1.PlaceName and c2.[Month] = c1.[Month] + 1
for xml path('CountryRow'), root('CountryValues')

----------------------------------
<CountryValues>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>1</Month>
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>2</Month>
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>3</Month>
    <PlaceValue>0</PlaceValue>
  </CountryRow>
  <CountryRow>
    <CountryName>Brazil</CountryName>
    <PlaceName>Place 1</PlaceName>
    <Month>4</Month>
    <PlaceValue>10</PlaceValue>
  </CountryRow>
</CountryValues>

Second way would be to use xquery inside the xml itself.

The answer is really depends on what do you mean by "immediately preceding PlaceValue > 1". I've assumed here that this means - month right before month with value > 1.




回答2:


If you are fine using C# you should look at XDocument.

Example :

XDocument document = XDocument.Parse("<CountryValues><CountryRow><CountryName>Brazil</CountryName><PlaceName>Place 1</PlaceName><Month>1</Month><PlaceValue>0</PlaceValue>        </CountryRow><CountryRow><CountryName>Brazil</CountryName><PlaceName>Place 1</PlaceName><Month>2</Month><PlaceValue>0</PlaceValue>        </CountryRow><CountryRow><CountryName>Brazil</CountryName><PlaceName>Place 1</PlaceName><Month>3</Month><PlaceValue>0</PlaceValue>        </CountryRow><CountryRow><CountryName>Brazil</CountryName><PlaceName>Place 1</PlaceName><Month>4</Month><PlaceValue>10</PlaceValue>        </CountryRow><CountryRow><CountryName>Australia</CountryName><PlaceName>Place 1</PlaceName><Month>1</Month><PlaceValue>0</PlaceValue>        </CountryRow><CountryRow><CountryName>Australia</CountryName><PlaceName>Place 1</PlaceName><Month>1</Month><PlaceValue>0</PlaceValue>        </CountryRow><CountryRow><CountryName>Australia</CountryName><PlaceName>Place 1</PlaceName><Month>1</Month><PlaceValue>4</PlaceValue>        </CountryRow></CountryValues>");
var countries = document.Root.Elements("CountryRow");

foreach (var country in countries)
{
    //Do your work here

    //You can access sub element like so :
    Console.WriteLine (country.Element("CountryName").Value);
}

Output:

Brazil
Brazil
Brazil
Brazil
Australia
Australia
Australia



回答3:


You need to use the .modify() method of the xml data type (see here: https://msdn.microsoft.com/en-us/library/ms187093.aspx) and XQuery to find which nodes you should change. You can find some nice examples in this link: http://www.mssqltips.com/sqlservertip/2738/examples-of-using-xquery-to-update-xml-data-in-sql-server/



来源:https://stackoverflow.com/questions/29756912/how-to-update-xml-in-sql-based-on-values-in-that-xml

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!