-3

Suppose there are two tables

TableA

Id XML

1 <A x="$Y" B="$Z" />

TableB

Id Key Value

1  $Y   100

1  $Z   200

Id columns in TableA and TableB can be joined.

The expected output is

TableA

Id XML

1 <A x="100" B="200" />
Thom A
  • 88,727
  • 11
  • 45
  • 75
userx
  • 3,713
  • 5
  • 32
  • 38
  • 1
    What have you tried so far? What's wrong with `REPLACE`? – Thom A Sep 04 '19 at 15:40
  • @Larnu - Replace is not letting me make use of ID columns efficiently. I have to first get the String dynamically (understand where does $ start and when the quotes end) and then go to another table and then match the string. Considerably slow. – userx Sep 04 '19 at 15:43
  • https://stackoverflow.com/questions/1956978/string-replacement-in-sql-server-using-the-contents-of-another-table-e-g-updat and https://stackoverflow.com/questions/51835718/how-to-replace-substring-in-a-column-using-lookup-table-in-sql-server – Tab Alleman Sep 04 '19 at 15:44
  • @TabAlleman - Let me go through them. – userx Sep 04 '19 at 15:46
  • @Larnu - Can we join these tables on a TableA.ID = TableB.ID in REPLACE function ? – userx Sep 04 '19 at 15:50
  • inside the `REPLACE`, no; in your `FROM`, yes. – Thom A Sep 04 '19 at 16:00

1 Answers1

1

The link supplied by Tab Alleman does show you the basic idea here. You need to use REPLACE and a Recursive Common Table Expression (rCTE):

CREATE TABLE TableA (Id int,
                     [XML] xml);
CREATE TABLE TableB (Id int,
                     [Key] varchar(5),
                     [Value] varchar(20));
INSERT INTO dbo.TableA (Id,
                        [XML])
VALUES(1,'<A x="$Y" B="$Z" />');

INSERT INTO dbo.TableB (Id,
                        [Key],
                        [Value])
VALUES (1,'$Y','100'),
       (1,'$Z','200');
GO


WITH RNs AS(
    SELECT B.Id,
           B.[Key],
           B.[Value],
           ROW_NUMBER() OVER (PARTITION BY B.Id ORDER BY [Key]) AS RN
    FROM dbo.TableB B),
rCTE AS(
    SELECT A.Id,
           REPLACE(CONVERT(varchar(MAX),A.[XML]),RN.[Key], RN.[Value]) AS [XML],
           RN.RN
    FROM dbo.TableA A
         JOIN RNs RN ON A.Id = RN.Id
    WHERE RN.RN = 1
    UNION ALL
    SELECT r.Id,
           REPLACE(r.[XML],RN.[Key], RN.[Value]) AS [XML],
           RN.RN
    FROM rCTE r
         JOIN RNs RN ON r.Id = RN.Id
                    AND RN.RN = r.RN + 1)
SELECT Id,
       CONVERT(xml,[xml]) AS [XML]
FROM rCTE r
WHERE RN = (SELECT MAX(RN) FROM rCTE E);

GO

DROP TABLE dbo.TableA;
DROP TABLE dbo.TableB;

As an UPDATE statement, this would like this:

WITH RNs AS(
    SELECT B.Id,
           B.[Key],
           B.[Value],
           ROW_NUMBER() OVER (PARTITION BY B.Id ORDER BY [Key]) AS RN
    FROM dbo.TableB B),
rCTE AS(
    SELECT A.Id,
           REPLACE(CONVERT(varchar(MAX),A.[XML]),RN.[Key], RN.[Value]) AS [XML],
           RN.RN
    FROM dbo.TableA A
         JOIN RNs RN ON A.Id = RN.Id
    WHERE RN.RN = 1
    UNION ALL
    SELECT r.Id,
           REPLACE(r.[XML],RN.[Key], RN.[Value]) AS [XML],
           RN.RN
    FROM rCTE r
         JOIN RNs RN ON r.Id = RN.Id
                    AND RN.RN = r.RN + 1)
UPDATE A
SET [XML] = r.[XML]
FROM TableA A
     JOIN rCTE r oN A.Id = r.Id
WHERE RN = (SELECT MAX(RN) FROM rCTE E);
Thom A
  • 88,727
  • 11
  • 45
  • 75
  • I am just trying to run update dbo.TableA set a.[XML] = REPLACE(convert (varchar(max),a.[XML]), b.[key], b.[value]) as [xml] from dbo.tablea a inner join dbo.tableb b on a.id = b.id but it says invalid syntax near 'as', isn't it intending to do the same operation ? – userx Sep 04 '19 at 17:01
  • You don't alias a column in an `UPDATE` statement, @Gsab – Thom A Sep 04 '19 at 17:02
  • I've added an `UPDATE` version for you, @Gsab – Thom A Sep 04 '19 at 17:06
  • @Larnu- fixed the statement like update dbo.TableA set [XML] = REPLACE(convert (varchar(max),a.[XML]), b.[key], b.[value]) from dbo.tablea a inner join dbo.tableb b on a.id = b.id , but what i have observed is that only the first key got replaced with its value and not the second – userx Sep 04 '19 at 17:08
  • It works fine for me, @Gsab: [db<>fiddle](https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=18672e453483d0fbd149498749b3d2b7) – Thom A Sep 04 '19 at 17:10
  • when we run just the update statement without actually going for a recursive cte. I mean just single update statement, it only replaces first key. Standalone statement like update dbo.TableA set [XML] = REPLACE(convert (varchar(max),a.[XML]), b.[key], b.[value]) from dbo.tablea a inner join dbo.tableb b on a.id = b.id – userx Sep 04 '19 at 17:11
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/198958/discussion-between-gsab-and-larnu). – userx Sep 04 '19 at 17:13
  • well yes, @Gsab, if you exclude the rCTE of source it won't work correctly... That's why there's an rCTE... – Thom A Sep 04 '19 at 17:20