Randy Hall
Randy Hall

Reputation: 8167

Replace email inside varchar tsql

I have an email address inside stringified JSON, in a column.

I need to replace it with a different email address.

I should add that I cannot use CLR for this. That would be too easy.

I'll take other suggestions, but the best I've come up with is to replace everything between the "s before and after the @.

DECLARE @testEmailAddress varchar(255);
SET @testEmailAddress = '[email protected]'; 
UPDATE #TestEmails
SET 
    COMM_DATA = 
        STUFF (
            COMM_DATA,      
            PATINDEX(
                '%@%', 
                COMM_DATA
            ) 
            -
            PATINDEX ('%"%',
                REVERSE(
                    SUBSTRING(
                        COMM_DATA, 
                        0, 
                        PATINDEX(
                            '%@%', 
                            COMM_DATA
                        )
                    )
                )
            ) + 1,
            PATINDEX(
                '%@%', 
                COMM_DATA
            ) 
            +
            PATINDEX ('%"%',
                SUBSTRING(
                    COMM_DATA, 
                    PATINDEX(
                        '%@%', 
                        COMM_DATA
                    ),
                    LEN(COMM_DATA)
                )
            ) - 1,
            @testEmailAddress
        )
;

Which almost works, except that the replacement email address seems to be overrunning the replacement string by several characters.

For example

{"CustomerEmail":"[email protected]","property2":"value2","property3":"value3","property4":"value4","property5":"value5","property6":"value6","property7":"value7","property8":"value8"}

becomes

{"CutomerEmail":"[email protected]":"value8"}

but I expect it to be

{"CustomerEmail":"[email protected]","property2":"value2","property3":"value3","property4":"value4","property5":"value5","property6":"value6","property7":"value7","property8":"value8"}

Upvotes: 1

Views: 77

Answers (3)

djangojazz
djangojazz

Reputation: 13252

I would do something more like this: Although I bet in SQL 2016 there is a better way to query and replace data than plain string parsing.

DECLARE @replace varchar(255) = '"[email protected]"'; 

DECLARE @JSON VARCHAR(4000) = '{"CustomerEmail":"[email protected]","property2":"value2","property3":"value3","property4":"value4","property5":"value5","property6":"value6","property7":"value7","property8":"value8"}'

DECLARE @Search VARCHAR(128) = '"CustomerEmail"'

DECLARE
    @SearchStart  INT
,   @SearchEnd    INT
,   @ValStart     INT
,   @ValEnd       INT
;

SELECT 
    @SearchStart = CHARINDEX(@Search, @JSON, 1) 
,   @SearchEnd = CHARINDEX(@Search, @JSON, 1) + LEN(@Search) 
,   @ValStart = CHARINDEX(':', @JSON, CHARINDEX(@Search, @JSON, 1) + LEN(@Search)) + 1 
,   @ValEnd = CHARINDEX('"', @JSON, CHARINDEX(@Search, @JSON, 1) + LEN(@Search) + 2) + 1 

SELECT STUFF(@Json, @ValStart, @ValEnd - @ValStart, @replace)

I could essentially make this a scalar function that takes a string(varchar JSON) and returns an altered JSON. You essentially are just key pair hunting in a string. That way I could run it against a result set and do it N number of times and just require a parameter of what field I was searching(@JSON), what I was 'Key' I was searching to replace(@CustomerEmail), and what I was replacing it with(@replace).

Upvotes: 1

Serg
Serg

Reputation: 22811

CROSS APLLY is handy for calculations like this one

declare @t table (comm_data varchar(max))
insert @t values
('{"CustomerEmail":"[email protected]","property2":"value2","property3":"value3","property4":"value4","property5":"value5","property6":"value6","property7":"value7","property8":"value8"}');

DECLARE @testEmailAddress varchar(255);
SET @testEmailAddress = '[email protected]'; 
select t1.*, t2.*, stuff(comm_data,p1-p2+2,p2+p3-3,@testEmailAddress)
from @t 
cross apply (
     select p1=PATINDEX('%@%',COMM_DATA)
    ) t1
cross apply (
     select p2=PATINDEX('%":"%', reverse(left(COMM_DATA,p1))),
       p3 = PATINDEX('%","%', substring(COMM_DATA, p1, len(COMM_DATA)))
    ) t2

You can easily debug proper p1-p2+2,p2+p3-3 stuff bounds and convert the code to a long form if needed.

Upvotes: 1

Randy Hall
Randy Hall

Reputation: 8167

I was miscalculating the end point. Instead of giving the distance between the start and end points for the STUFF, I was giving it the starting and ending positions from the start of the string. Final code looks like:

    COMM_DATA = 
        STUFF (
            COMM_DATA,      
            PATINDEX(
                '%@%', 
                COMM_DATA
            ) 
            -
            PATINDEX ('%"%',
                REVERSE(
                    SUBSTRING(
                        COMM_DATA, 
                        0, 
                        PATINDEX(
                            '%@%', 
                            COMM_DATA
                        )
                    )
                )
            )+ 1,
            PATINDEX(
                '%@%', 
                COMM_DATA
            ) 
            +
            PATINDEX ('%"%',
                SUBSTRING(
                    COMM_DATA, 
                    PATINDEX(
                        '%@%', 
                        COMM_DATA
                    ),
                    LEN(COMM_DATA)
                )
            )
            -
            (
                PATINDEX(
                    '%@%', 
                    COMM_DATA
                ) 
                -
                PATINDEX ('%"%',
                    REVERSE(
                        SUBSTRING(
                            COMM_DATA, 
                            0, 
                            PATINDEX(
                                '%@%', 
                                COMM_DATA
                            )
                        )
                    )
                )
            ) - 2,
            @testEmailAddress
        )

Upvotes: 0

Related Questions