I have a table like :
TemplateBody
---------------------------------------------------------------------
1.This is To inform #FirstName# about the issues regarding #Location#
Here the key string are #FirstName#
and #Location#
which are distinguished by hash tags.
I have another table with the replacement values:
Variables | TemplateValues
-----------------------------
1.#FirstName# | Joseph William
2.#Location# | Alaska
I need to replace these two key strings with their values in the first table.
There are several ways this can be done. I'll list two ways. Each one has advantages and disadvantages. I would personally use the first one (Dynamic SQL).
1. Dynamic SQL
- Advantages: Fast, doesn't require recursion
- Disadvantages: Can't be used to update table variables
2. Recursive CTE
- Advantages: Allows updates of table variables
- Disadvantages: Requires recursion and is memory intensive, recursive CTE's are slow
1.A. Dynamic SQL: Regular tables and Temporary tables.
This example uses a temporary table as the text source:
CREATE TABLE #tt_text(templatebody VARCHAR(MAX));
INSERT INTO #tt_text(templatebody)VALUES
('This is to inform #first_name# about the issues regarding #location#');
CREATE TABLE #tt_repl(variable VARCHAR(256),template_value VARCHAR(8000));
INSERT INTO #tt_repl(variable,template_value)VALUES
('#first_name#','Joseph William'),
('#location#','Alaska');
DECLARE @rep_call NVARCHAR(MAX)='templatebody';
SELECT
@rep_call='REPLACE('+@rep_call+','''+REPLACE(variable,'''','''''')+''','''+REPLACE(template_value,'''','''''')+''')'
FROM
#tt_repl;
DECLARE @stmt NVARCHAR(MAX)='SELECT '+@rep_call+' FROM #tt_text';
EXEC sp_executesql @stmt;
/* Use these statements if you want to UPDATE the source rather than SELECT from it
DECLARE @stmt NVARCHAR(MAX)='UPDATE #tt_text SET templatebody='+@rep_call;
EXEC sp_executesql @stmt;
SELECT * FROM #tt_text;*/
DROP TABLE #tt_repl;
DROP TABLE #tt_text;
1.B. Dynamic SQL: Table variables.
Requires to have the table defined as a specific table type. Example type definition:
CREATE TYPE dbo.TEXT_TABLE AS TABLE(
id INT IDENTITY(1,1) PRIMARY KEY,
templatebody VARCHAR(MAX)
);
GO
Define a table variable of this type, and use it in a Dynamic SQL statement as follows. Note that updating a table variable this way is not possible.
DECLARE @tt_text dbo.TEXT_TABLE;
INSERT INTO @tt_text(templatebody)VALUES
('This is to inform #first_name# about the issues regarding #location#');
DECLARE @tt_repl TABLE(id INT IDENTITY(1,1),variable VARCHAR(256),template_value VARCHAR(8000));
INSERT INTO @tt_repl(variable,template_value)VALUES
('#first_name#','Joseph William'),
('#location#','Alaska');
DECLARE @rep_call NVARCHAR(MAX)='templatebody';
SELECT
@rep_call='REPLACE('+@rep_call+','''+REPLACE(variable,'''','''''')+''','''+REPLACE(template_value,'''','''''')+''')'
FROM
@tt_repl;
DECLARE @stmt NVARCHAR(MAX)='SELECT '+@rep_call+' FROM @tt_text';
EXEC sp_executesql @stmt,N'@tt_text TEXT_TABLE READONLY',@tt_text;
2. Recursive CTE:
The only reasons why you would write this using a recursive CTE is that you intend to update a table variable, or you are not allowed to use Dynamic SQL somehow (eg company policy?).
Note that the default maximum recursion level is 100. If you have more than a 100 replacement variables you should increase this level by adding OPTION(MAXRECURSION 32767)
at the end of the query (see Query Hints - MAXRECURSION
).
DECLARE @tt_text TABLE(id INT IDENTITY(1,1),templatebody VARCHAR(MAX));
INSERT INTO @tt_text(templatebody)VALUES
('This is to inform #first_name# about the issues regarding #location#');
DECLARE @tt_repl TABLE(id INT IDENTITY(1,1),variable VARCHAR(256),template_value VARCHAR(8000));
INSERT INTO @tt_repl(variable,template_value)VALUES
('#first_name#','Joseph William'),
('#location#','Alaska');
;WITH cte AS (
SELECT
t.id,
l=1,
templatebody=REPLACE(t.templatebody,r.variable,r.template_value)
FROM
@tt_text AS t
INNER JOIN @tt_repl AS r ON r.id=1
UNION ALL
SELECT
t.id,
l=l+1,
templatebody=REPLACE(t.templatebody,r.variable,r.template_value)
FROM
cte AS t
INNER JOIN @tt_repl AS r ON r.id=t.l+1
)
UPDATE
@tt_text
SET
templatebody=cte.templatebody
FROM
@tt_text AS t
INNER JOIN cte ON
cte.id=t.id
WHERE
cte.l=(SELECT MAX(id) FROM @tt_repl);
/* -- if instead you wanted to select the replaced strings, comment out
-- the above UPDATE statement, and uncomment this SELECT statement:
SELECT
templatebody
FROM
cte
WHERE
l=(SELECT MAX(id) FROM @tt_repl);*/
SELECT*FROM @tt_text;
As long as the values for the Variables are unique ('#FirstName#' etc.) you can join each variable to the table containing TemplateBody:
select replace(replace(t.TemplateBody,'#FirstName#',variable.theVariable),'#Location#',variable2.theVariable)
from
[TemplateBodyTable] t
left join
(
select TemplateValues theVariable,Variables
from [VariablesTable] v
) variable on variable.Variables='#FirstName#'
left join
(
select TemplateValues theVariable,Variables
from [VariablesTable] v
) variable2 on variable2.Variables='#Location#'
A common table expression would allow you to loop through your templates and replace all variables in that template using a variables table. If you have a lot of variables, the level of recursion might go beyond the default limit of 100 recursions. You can play with the MAXRECURSION
option according to your need.
DECLARE @Templates TABLE(Body nvarchar(max));
INSERT INTO @Templates VALUES ('This is to inform #FirstName# about the issues regarding #Location#');
DECLARE @Variables TABLE(Name nvarchar(50), Value nvarchar(max));
INSERT INTO @Variables VALUES ('#FirstName#', 'Joseph William'),
('#Location#', 'Alaska');
WITH replacing(Body, Level) AS
(
SELECT t.Body, 1 FROM @Templates t
UNION ALL
SELECT REPLACE(t.Body, v.Name, v.Value), t.Level + 1
FROM replacing t INNER JOIN @Variables v ON PATINDEX('%' + v.Name + '%', t.Body) > 0
)
SELECT TOP 1 r.Body
FROM replacing r
WHERE r.Level = (SELECT MAX(Level) FROM replacing)
OPTION (MAXRECURSION 0);