Search code examples
sqlsql-serversqlclr

sql server, replace chars in string with values in table


how can i replace values in string with values that are in a table?

for example

    select *
into #t
from 
(
    select 'bla'c1,'' c2 union all
    select 'table'c1,'TABLE' c2 union all
    select 'value'c1,'000' c2 union all
    select '...'c1,'' c2
)t1

declare @s nvarchaR(max)='this my string and i want to replace all values that are in table #t'

i have some values in my table and i want to replace C1 with C2 in my string.

the results should be

this my string and i want to replace all 000 that are in TABLE #t

UPDATE: i solved with a CLR

using System;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Data.Linq;

namespace ReplaceValues
{
    public partial class Functions
    {
        [SqlFunction
            (
                //DataAccess = DataAccessKind.Read,
                SystemDataAccess = SystemDataAccessKind.Read
            )
        ]
        public static string ReplaceValues(string row, string delimitator, string values, string replace/*, bool CaseSensitive*/)
        {
            //return row;
            string[] tmp_values = values.Split(new string[] { delimitator }, StringSplitOptions.None);
            string[] tmp_replace = replace.Split(new string[] { delimitator }, StringSplitOptions.None);

            row = row.ToUpper();

            for (int i = 0; i < Math.Min(tmp_values.Length, tmp_replace.Length); i++)
            {
                row = row.Replace(tmp_values[i].ToUpper(), tmp_replace[i]);
            }

            return row;
        }

    }
}

and then

select *
into #t
from 
(
    select 'value1'OldValue,'one'NewValue union all
    select 'value2'OldValue,'two'NewValue union all
    select 'value3'OldValue,'three'NewValue union all
    select 'value4'OldValue,'four'NewValue
)t1


select dbo.ReplaceValues(t1.column,'|',t2.v,t2.r)
from MyTable t1
cross apply
(
    select dbo.inlineaggr(i1.OldValue,'|',1,1)v,
           dbo.inlineaggr(i1.NewValue,'|',1,1)r
    from #t i1
)t2

i have to improved it to manage better the case sensitive, but performance are not bad. (also 'inlineaggr' is a CLR i wrote years ago)


Solution

  • You can do this via recursion. Assuming you have a table of find-replace pairs, you can number the rows and then use recursive cte:

    create table #t(c1 nvarchar(100), c2 nvarchar(100));
    insert into #t(c1, c2) values
    ('bla', ''),
    ('table', 'table'),
    ('value', '000'),
    ('...', '');
    
    declare @s nvarchar(max) = 'this my string and i want to replace all values that are in table #t';
    
    with ncte as (
        select row_number() over (order by (select null)) as rn, *
        from #t
    ), rcte as (
        select rn, replace(@s, c1, c2) as newstr
        from ncte
        where rn = 1
    
        union all
    
        select ncte.rn, replace(rcte.newstr, ncte.c1, ncte.c2)
        from ncte
        join rcte on ncte.rn = rcte.rn + 1
    )
    select *
    from rcte
    where rn = 4