Вы можете руководствоваться следующим примером. Мы используем рекурсивный CTE для замены сообщения каждым значением-заполнителем, а затем показываем только последнюю версию.
IF OBJECT_ID('tempdb..#Message') IS NOT NULL
DROP TABLE #Message
CREATE TABLE #Message (
MessageID INT,
Message VARCHAR(200))
INSERT INTO #Message (
MessageID,
Message)
VALUES
(1, 'First name:{0}; Last name:{1}'),
(2, 'Address: {0}; City: {1}; Country: {2}')
IF OBJECT_ID('tempdb..#Text') IS NOT NULL
DROP TABLE #Text
CREATE TABLE #Text (
MessageID INT,
TextPlaceholder INT,
Text VARCHAR(100)
PRIMARY KEY (MessageID, TextPlaceholder))
INSERT INTO #Text (
MessageID,
TextPlaceholder,
Text)
VALUES
(1, 0, 'John'),
(1, 1, 'Doe'),
(2, 0, 'Maple Str. 1'),
(2, 1, 'New York'),
(2, 2, 'USA')
;WITH MaxPlaceholderByMessage AS
(
SELECT
M.MessageID,
MaxPlaceholder = MAX(T.TextPlaceholder)
FROM
#Message AS M
INNER JOIN #Text AS T ON M.MessageID = T.MessageID
GROUP BY
M.MessageID
),
RecursiveReplacePlaceholders AS
(
SELECT
MessageID = M.MessageID,
ReplacedMessage = CONVERT(VARCHAR(MAX), M.Message),
CurrentPlaceholder = -1
FROM
#Message AS M
UNION ALL
SELECT
MessageID = R.MessageID,
ReplacedMessage = REPLACE(
R.ReplacedMessage,
'{' + CONVERT(VARCHAR(10), T.TextPlaceholder) + '}',
T.Text),
CurrentPlaceholder = T.TextPlaceholder
FROM
RecursiveReplacePlaceholders AS R
INNER JOIN #Text AS T ON
R.MessageID = T.MessageID AND
R.CurrentPlaceholder + 1 = T.TextPlaceholder
)
SELECT
R.MessageID,
R.ReplacedMessage
FROM
RecursiveReplacePlaceholders AS R
LEFT JOIN MaxPlaceholderByMessage AS M ON R.MessageID = M.MessageID
WHERE
M.MaxPlaceholder = R.CurrentPlaceholder OR
M.MessageID IS NULL -- There is no text available to replace
ORDER BY
R.MessageID
/*
Result:
MessageID ReplacedMessage
1 First name:John; Last name:Doe
2 Address: Maple Str. 1; City: New York; Country: USA
*/
Если у вас отсутствуют тексты для заполнителя, вам придется немного изменить рекурсивное условие для циклического перехода до максимального доступного заполнителя.