SQL Server: How to update data by Xquery

T.Zacks 3,996 Reputation points
2022-05-21T09:17:56.45+00:00

This way my xml looks like which is stored in a column whose type is xml

<?xml version="1.0" standalone="yes"?>  
<AVLR_ViewAll>  
  <dgvViewAll_Vertical>  
    <Section_x0020_>3R</Section_x0020_>  
    <LineItem>Berenberg Bank</LineItem>  
    <Revise_x0020_Date>05-19-2022</Revise_x0020_Date>  
    <_x0032_011_x0020_FYA />  
    <_x0032_012_x0020_FYA />  
    <_x0032_013_x0020_FYA />  
    <_x0031_Q_x0020_2014A />  
    <_x0032_Q_x0020_2014A />  
    <_x0033_Q_x0020_2014A />  
    <_x0034_Q_x0020_2014A />  
    <_x0032_014_x0020_FYA />  
    <_x0031_Q_x0020_2015A />  
    <_x0032_Q_x0020_2015A />  
    <_x0033_Q_x0020_2015A />  
    <_x0034_Q_x0020_2015A />  
    <_x0032_015_x0020_FYA />  
    <_x0031_Q_x0020_2016A />  
    <_x0032_Q_x0020_2016A />  
    <_x0033_Q_x0020_2016A />  
    <_x0034_Q_x0020_2016A />  
    <_x0032_016_x0020_FYA>$167.4260</_x0032_016_x0020_FYA>  
    <_x0031_Q_x0020_2017A>$48.9650</_x0031_Q_x0020_2017A>  
    <_x0032_Q_x0020_2017A>$50.8910</_x0032_Q_x0020_2017A>  
    <_x0033_Q_x0020_2017A>$55.2680</_x0033_Q_x0020_2017A>  
    <_x0034_Q_x0020_2017A>$58.0350</_x0034_Q_x0020_2017A>  
    <_x0032_017_x0020_FYA>$213.1590</_x0032_017_x0020_FYA>  
    <_x0031_Q_x0020_2018A>$61.3770</_x0031_Q_x0020_2018A>  
    <_x0032_Q_x0020_2018A>$63.7090</_x0032_Q_x0020_2018A>  
    <_x0033_Q_x0020_2018A>$69.5270</_x0033_Q_x0020_2018A>  
    <_x0034_Q_x0020_2018A>$76.9230</_x0034_Q_x0020_2018A>  
    <_x0032_018_x0020_FYA>$271.5360</_x0032_018_x0020_FYA>  
    <_x0031_Q_x0020_2019A>$84.9700</_x0031_Q_x0020_2019A>  
    <_x0032_Q_x0020_2019A>$91.2990</_x0032_Q_x0020_2019A>  
    <_x0033_Q_x0020_2019A>$98.5250</_x0033_Q_x0020_2019A>  
    <_x0034_Q_x0020_2019A>$107.6270</_x0034_Q_x0020_2019A>  
    <_x0032_019_x0020_FYA>$382.4210</_x0032_019_x0020_FYA>  
    <_x0031_Q_x0020_2020A>$111.4430</_x0031_Q_x0020_2020A>  
    <_x0032_Q_x0020_2020A>$116.4870</_x0032_Q_x0020_2020A>  
    <_x0033_Q_x0020_2020A>$127.8790</_x0033_Q_x0020_2020A>  
    <_x0034_Q_x0020_2020A>$144.7600</_x0034_Q_x0020_2020A>  
    <_x0032_020_x0020_FYA>$500.5690</_x0032_020_x0020_FYA>  
    <_x0031_Q_x0020_2021A>$153.6010</_x0031_Q_x0020_2021A>  
    <_x0032_Q_x0020_2021A>$169.0670</_x0032_Q_x0020_2021A>  
    <_x0033_Q_x0020_2021A>$181.2000</_x0033_Q_x0020_2021A>  
    <_x0034_Q_x0020_2021A>$195.1420</_x0034_Q_x0020_2021A>  
    <_x0032_021_x0020_FYA>$699.0100</_x0032_021_x0020_FYA>  
    <_x0031_Q_x0020_2022A>$204.5300</_x0031_Q_x0020_2022A>  
    <_x0032_Q_x0020_2022E>$210.9942</_x0032_Q_x0020_2022E>  
    <_x0033_Q_x0020_2022E>$224.5874</_x0033_Q_x0020_2022E>  
    <_x0034_Q_x0020_2022E>$235.3893</_x0034_Q_x0020_2022E>  
    <_x0032_022_x0020_FYE>$875.5010</_x0032_022_x0020_FYE>  
    <_x0031_Q_x0020_2023E>$251.5889</_x0031_Q_x0020_2023E>  
    <_x0032_Q_x0020_2023E>$263.7800</_x0032_Q_x0020_2023E>  
    <_x0033_Q_x0020_2023E>$275.1985</_x0033_Q_x0020_2023E>  
    <_x0034_Q_x0020_2023E>$289.7587</_x0034_Q_x0020_2023E>  
    <_x0032_023_x0020_FYE>$1080.3263</_x0032_023_x0020_FYE>  
    <_x0031_Q_x0020_2024E>$311.6123</_x0031_Q_x0020_2024E>  
    <_x0032_Q_x0020_2024E>$326.4485</_x0032_Q_x0020_2024E>  
    <_x0033_Q_x0020_2024E>$340.7900</_x0033_Q_x0020_2024E>  
    <_x0034_Q_x0020_2024E>$358.7854</_x0034_Q_x0020_2024E>  
    <_x0032_024_x0020_FYE>$1337.6364</_x0032_024_x0020_FYE>  
    <_x0031_Q_x0020_2025E>$380.1615</_x0031_Q_x0020_2025E>  
    <_x0032_Q_x0020_2025E>$398.9651</_x0032_Q_x0020_2025E>  
    <_x0033_Q_x0020_2025E>$417.4265</_x0033_Q_x0020_2025E>  
    <_x0034_Q_x0020_2025E>$440.5300</_x0034_Q_x0020_2025E>  
    <_x0032_025_x0020_FYE>$1637.0833</_x0032_025_x0020_FYE>  
    <_x0031_Q_x0020_2026E>$466.9758</_x0031_Q_x0020_2026E>  
    <_x0032_Q_x0020_2026E>$491.0905</_x0032_Q_x0020_2026E>  
    <_x0033_Q_x0020_2026E>$514.9926</_x0033_Q_x0020_2026E>  
    <_x0034_Q_x0020_2026E>$544.7345</_x0034_Q_x0020_2026E>  
    <_x0032_026_x0020_FYE>$2017.7936</_x0032_026_x0020_FYE>  
    <_x0031_Q_x0020_2027E />  
    <_x0032_Q_x0020_2027E />  
    <_x0033_Q_x0020_2027E />  
    <_x0034_Q_x0020_2027E />  
    <_x0032_027_x0020_FYE />  
    <_x0031_Q_x0020_2028E />  
    <_x0032_Q_x0020_2028E />  
    <_x0033_Q_x0020_2028E />  
    <_x0034_Q_x0020_2028E />  
    <_x0032_028_x0020_FYE />  
    <GroupKey>Consensus Model~Total Revenue~TRIN~NBM~~1~3R</GroupKey>  
  </dgvViewAll_Vertical>  
  </AVLR_ViewAll>  

Now i want to replace data in GroupKey Tag

this is my GroupKey Tag <GroupKey>Consensus Model~Total Revenue~TRIN~NBM~~1~3R</GroupKey>

i want to replace this ~Total Revenue~ from ~Gross Revenue~ in Group Key Tag

so data in Group Key Tag would look like
<GroupKey>Consensus Model~GrossRevenue~TRIN~NBM~~1~3R</GroupKey>

here data is divided by ~ sign

so tell me how could i update data in xml by xquery ?

https://learn.microsoft.com/en-us/answers/questions/157794/index.html
looking for a sample code. Thanks

Transact-SQL
Transact-SQL
A Microsoft extension to the ANSI SQL language that includes procedural programming, local variables, and various support functions.
4,675 questions
{count} votes

Accepted answer
  1. Yitzhak Khabinsky 26,296 Reputation points
    2022-05-22T01:34:07.527+00:00

    Hi @T.Zacks ,

    Please try the following.

    SQL

    -- DDL and sample data population, start  
    DECLARE @tbl TABLE  (ID INT IDENTITY PRIMARY KEY, xmldata XML);  
    INSERT INTO @tbl (xmldata) VALUES  
    (N'<AVLR_ViewAll>  
     <dgvViewAll_Vertical>  
     <Section_x0020_>3R</Section_x0020_>  
     <LineItem>Berenberg Bank</LineItem>  
     <Revise_x0020_Date>05-19-2022</Revise_x0020_Date>  
     <GroupKey>Consensus Model~Total Revenue~TRIN~NBM~~1~3R</GroupKey>  
     </dgvViewAll_Vertical>  
    </AVLR_ViewAll>');  
    -- DDL and sample data population, end  
      
    DECLARE @from VARCHAR(30) = '~Total Revenue~'  
     , @to VARCHAR(30) = '~Gross Revenue~';  
      
    -- before  
    SELECT * FROM @tbl;  
      
    UPDATE t  
    SET xmldata.modify('replace value of (/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey/text())[1]  
     with (sql:column("t1.c"))')  
    FROM @tbl AS t  
    CROSS APPLY (SELECT REPLACE(xmldata.value('(/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey/text())[1]', 'VARCHAR(100)'),@from,@to)) AS t1(c)  
    WHERE CHARINDEX(@from, xmldata.value('(/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey/text())[1]', 'VARCHAR(MAX)'))>0;  
      
    -- after  
    SELECT * FROM @tbl;  
    
    1 person found this answer helpful.
    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Yitzhak Khabinsky 26,296 Reputation points
    2022-05-22T14:59:25.133+00:00

    Hi @T.Zacks ,

    If each table row contains XML with multiple <dgvViewAll_Vertical> tags, it is a slightly different approach.

    SQL

    -- DDL and sample data population, start  
    DECLARE @tbl TABLE  (ID INT IDENTITY PRIMARY KEY, xmldata XML);  
    INSERT INTO @tbl (xmldata) VALUES  
    (N'<AVLR_ViewAll>  
     <dgvViewAll_Vertical>  
     <Section_x0020_>3R</Section_x0020_>  
     <LineItem>Berenberg Bank</LineItem>  
     <Revise_x0020_Date>05-19-2022</Revise_x0020_Date>  
     <GroupKey>Consensus Model~Total Revenue~TRIN~NBM~~1~3R</GroupKey>  
     </dgvViewAll_Vertical>  
     <dgvViewAll_Vertical>  
     <Section_x0020_>CL</Section_x0020_>  
     <LineItem>Deutsche Bank</LineItem>  
     <Revise_x0020_Date>02-28-2020</Revise_x0020_Date>  
     <GroupKey>Segment Detail~Total Revenue~RD_100~NBM~~1~CL</GroupKey>  
      </dgvViewAll_Vertical>  
    </AVLR_ViewAll>')  
    , (N'<root/>')  
    , (N'<AVLR_ViewAll>  
     <dgvViewAll_Vertical>  
     <Section_x0020_>18R</Section_x0020_>  
     <LineItem>Discount Bank</LineItem>  
     <Revise_x0020_Date>2022-05-22</Revise_x0020_Date>  
     <GroupKey>Flop Model~Total Revenue~TRIN~NBM~~1~3R</GroupKey>  
     </dgvViewAll_Vertical>  
    </AVLR_ViewAll>');  
    -- DDL and sample data population, end  
      
    DECLARE @from VARCHAR(30) = '~Total Revenue~'  
     , @to VARCHAR(30) = '~Gross Revenue~';  
      
    -- before  
    SELECT * FROM @tbl  
    WHERE xmldata.exist('/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey[contains(text()[1], sql:variable("@from"))]') = 1;  
      
    DECLARE @UPDATE_STATUS BIT = 1;  
      
    WHILE @UPDATE_STATUS > 0  
    BEGIN  
     UPDATE t  
     SET xmldata.modify('replace value of (/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey[contains(text()[1], sql:variable("@from"))]/text())[1]  
     with (sql:column("t1.c"))')  
     FROM @tbl AS t  
     CROSS APPLY (SELECT REPLACE(xmldata.value('(/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey[contains(text()[1], sql:variable("@from"))]/text())[1]', 'VARCHAR(100)'),@from,@to)) AS t1(c)  
     WHERE xmldata.exist('/AVLR_ViewAll/dgvViewAll_Vertical/GroupKey[contains(text()[1], sql:variable("@from"))]') = 1;  
          
     SET @UPDATE_STATUS = @@ROWCOUNT;  
     PRINT @UPDATE_STATUS;  
    END;  
      
    -- after  
    SELECT * FROM @tbl;  
    
    1 person found this answer helpful.
    0 comments No comments

  2. T.Zacks 3,996 Reputation points
    2022-05-22T09:41:20.667+00:00

    Here is another approach

    DECLARE @xml xml
     DECLARE @li varchar(max) ='Segment Detail'
     DECLARE @newXfundCode varchar(max) ='TEST'
    
     CREATE TABLE #tmpData (id INT, xmldata xml)  
    
     INSERT INTO #tmpData (id, xmldata) 
     VALUES (1,N'<?xml version="1.0" encoding="utf-16"?>
     <PWR_ViewAll>
     <dgvViewAll_Vertical>
       <Section_x0020_>ZB-P1</Section_x0020_>
       <LineItem>B. Riley FBR Inc.</LineItem>
       <Revise_x0020_Date>08-21-2020</Revise_x0020_Date>
       <GroupKey>Consensus Model~Total Revenue~TRIN~NBM~~1~ZB-P1</GroupKey>
     </dgvViewAll_Vertical>
     <dgvViewAll_Vertical>
       <Section_x0020_>CL</Section_x0020_>
       <LineItem>Deutsche Bank</LineItem>
       <Revise_x0020_Date>02-28-2020</Revise_x0020_Date>
       <GroupKey>Segment Detail~Total Revenue~RD_100~NBM~~1~CL</GroupKey>
     </dgvViewAll_Vertical>
     </PWR_ViewAll>')
    
     SELECT @xml=xmldata from #tmpData where ID=1
    
     declare 
         anonymous userGroupKeyvalue varchar(100), 
         @newGroupKeyValue varchar(100);
    
     SELECT anonymous userGroupKeyvalue = col.value('(GroupKey/text())[1]', 'VARCHAR(MAX)')
     FROM @xml.nodes('/PWR_ViewAll/dgvViewAll_Vertical') AS tab (col)
     WHERE  CHARINDEX(@li, col.value('(GroupKey/text())[1]', 'VARCHAR(MAX)'))>0;
    
    
     --@newGroupKeyValue = anonymous usergroupkeyvalue but stuff/"replace" string between 2nd&3rd ~ with @newXfundCode
     select  @newGroupKeyValue = 
     --"replace/stuff" the old xfundcode := all chars from position of 2nd ~ (+1) till position of the 2nd ~ (minus 1) with an empty string
     stuff(anonymous userGroupKeyvalue, 
     --starting from position of 2nd ~ +1
     charindex('~', anonymous userGroupKeyvalue, charindex('~', anonymous userGroupKeyvalue, 1)+1)+1,
     --as many characters as exist between 3rd ~ and 2nd ~ (excluding ~)
     charindex('~', anonymous userGroupKeyvalue, charindex('~', anonymous userGroupKeyvalue, charindex('~', anonymous userGroupKeyvalue, 1)+1)+1)-1
     -
     charindex('~', anonymous userGroupKeyvalue, charindex('~', anonymous userGroupKeyvalue, 1)+1),
     --stuff the new xfundcode
     @newXfundCode
     );
    
     --test
     select anonymous userGroupKeyValue as oldgroupkeyvalue, @newGroupKeyValue as newgroupkeyvalue;
    
    
     --test
     select @xml as oldxml;
    
     --modify xml, replace the text of GroupKey element whose text equals anonymous userGroupKeyValue with @newGroupKeyValue
     --if newGroupKeyValue is not null etc...
     set @xml.modify('
       replace value of (/PWR_ViewAll/dgvViewAll_Vertical/GroupKey[.=sql:variable("anonymous userGroupKeyValue")]/text())[1]  
       with sql:variable("@newGroupKeyValue") 
     '
     );
    
     select @xml as newxml;
    
    
     --update #tmpData set xmldata = @xml where ID=1;
     --select * from #tmpData;
    
     Drop table #tmpData
    

    Here is 2nd approach

    DECLARE @xml xml
     DECLARE @li varchar(max) ='Consensus Model'
     DECLARE anonymous userXfundCode varchar(max) ='TRIN'
     DECLARE @newXfundCode varchar(max) ='TEST105'
     DECLARE @NewGroupKey NVARCHAR(max) 
    
     CREATE TABLE #tmpData (id INT, xmldata xml)  
    
      INSERT INTO #tmpData (id, xmldata) 
      VALUES (1,N'<?xml version="1.0" encoding="utf-16"?>
      <PWR_ViewAll>
      <dgvViewAll_Vertical>
        <Section_x0020_>ZB-P1</Section_x0020_>
        <LineItem>B. Riley FBR Inc.</LineItem>
        <Revise_x0020_Date>08-21-2020</Revise_x0020_Date>
        <GroupKey>Consensus Model~Total Revenue~TRIN~NBM~~1~ZB-P1</GroupKey>
      </dgvViewAll_Vertical>
      <dgvViewAll_Vertical>
        <Section_x0020_>CL</Section_x0020_>
        <LineItem>Deutsche Bank</LineItem>
        <Revise_x0020_Date>02-28-2020</Revise_x0020_Date>
        <GroupKey>Segment Detail~Total Revenue~RD_100~NBM~~1~CL</GroupKey>
      </dgvViewAll_Vertical>
      </PWR_ViewAll>')
    
     SELECT @xml=xmldata from #tmpData where ID=1
     DECLARE @r INT =1
     DECLARE @Cntr INT = 0
    
     CREATE TABLE #TempData1
     (
         ID    INT Identity,
         GroupKey NVARCHAR(MAX)
     )    
      DECLARE 
          anonymous userGroupKeyvalue nvarchar(100), 
          @newGroupKeyValue nvarchar(100),
          @GrpKey nvarchar(MAX);
    
      INSERT INTO #TempData1 
          SELECT col.value('(GroupKey/text())[1]', 'VARCHAR(MAX)')
          FROM @xml.nodes('/PWR_ViewAll/dgvViewAll_Vertical') AS tab (col)
          WHERE  CHARINDEX(@li, col.value('(GroupKey/text())[1]', 'VARCHAR(MAX)'))>0
    
     SELECT @Cntr = MAX(ID) FROM #TempData1
     While @r<=@Cntr
     BEGIN
         SELECT @GrpKey=GroupKey FROM #TempData1 WHERE ID=@r
    
         IF CHARINDEX(@li,@GrpKey) > 0 AND CHARINDEX(anonymous userXfundCode,@GrpKey) > 0
         BEGIN
    
             SET anonymous userGroupKeyValue=@GrpKey
    
             SELECT * INTO #tmpSingleRec FROM
             (SELECT ID,[DATA]  FROM SplitStringToTable(@GrpKey,'~')) a
    
             IF EXISTS(SELECT count(*) FROM #tmpSingleRec)
             BEGIN
                 UPDATE #tmpSingleRec SET DATA=@newXfundCode WHERE ID=3
                 SELECT  @NewGroupKey=COALESCE(@NewGroupKey + '~', '')+[DATA]  FROM #tmpSingleRec
    
                  set @xml.modify('
                    replace value of (/PWR_ViewAll/dgvViewAll_Vertical/GroupKey[.=sql:variable("anonymous userGroupKeyValue")]/text())[1]  
                    with sql:variable("@NewGroupKey") 
                  '
                  );
    
             END
         END
         SET @r=@r+1
     END
    
     UPDATE #tmpData SET xmldata=@xml WHERE ID=1
     SELECT * FROM #tmpData
    
    
     IF OBJECT_ID('TempDB..#TempData1') IS NOT NULL  
     BEGIN  
         drop table #TempData1  
     END  
     IF OBJECT_ID('TempDB..#tmpData') IS NOT NULL  
     BEGIN  
         drop table #tmpData  
     END  
     IF OBJECT_ID('TempDB..#tmpSingleRec') IS NOT NULL  
     BEGIN  
         drop table #tmpSingleRec  
     END  
    
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.