如何用多行数据替换字符串的多个部分?

you*_*gme 4 sql-server t-sql sql-server-2016 string-manipulation

给定一个包含两列的表 - 一个整数 ID 和一个基于文本的字符串 - 我想从一个字符串值开始,该字符串值对用大括号括起来的任意数量的整数进行编码,并与任何其他有效文本字符混合。

例子:'{1} / {9} ... {12}'

通过一条SELECT语句,我想返回一个字符串,其中所有整数(及其大括号)都已替换为从我的表派生的值;具体来说,行的文本值的 ID 与源字符串中找到的数字相匹配......并且大括号之外的任何字符保持不变。

这是一个未能完成任务的示例:

select
  replace('{13} {15}','{'+cast(id as varchar)+'}',isNull(display,''))
from testing;
Run Code Online (Sandbox Code Playgroud)

这将在表中每行返回 1 行testing。对于值 = 13 的行id,字符串的“{13}”部分已成功替换,但“{15}”部分未成功替换(第 15 行反之亦然)。

我想创建一个循环所有testing行并重复尝试替换的函数可以解决问题。尽管如此,直接的 SQL 语句比循环语句更好。

示例数据

+----+-------------------+
| id |  display          |
+----+-------------------+
|  1 |  Apple            |
|  2 |  Banana           |
|  3 |  Celery           |
|  4 |  Dragonfruit      |
|  5 |  Eggplant         |
|  6 |  Fenugreek        |
|  7 |  Gourd            |
|  8 |  Honeydew         |
|  9 |  Iceberg Lettuce  |
| 10 |  Jackfruit        |
| 11 |  Kale             |
| 12 |  Lemon            |
| 13 |  Mandarin         |
| 14 |  Nectarine        |
| 15 |  Olive            |
+----+-------------------+
Run Code Online (Sandbox Code Playgroud)

示例用例

select replace('{1} {3}',null,null) 
-- Returns 'Apple Celery'

select replace('{3},{4},{5}',null,null); 
-- Returns 'Celery,Dragonfruit,Eggplant'

select replace('{1} / {9} ... {12}',null,null); 
-- Returns 'Apple / Iceberg Lettuce ... Lemon'
Run Code Online (Sandbox Code Playgroud)

显然,replace关键字不起作用。

附言。如果解决方案需要更改字符串的格式以促进此目的,那么这是一个选项。

例如:('#1 / #9 ... #12'与前面的示例相关)

在这种格式中,也许我们可以将字符串分解为一个行集,基于 ,#获取left字符直到找到非数字,根据获取的数字到表中,将join和数字替换为表的值,然后将所有这些单独将标记修改回单个字符串?testing#testingdisplaystufffor xml path

我使用的是 SQL Server 2016,它不支持string_agg. 也就是说,如果有一个使用 的解决方案string_agg,我仍然有兴趣审查它。

Sco*_*red 5

cte这是使用递归来翻译变量的示例

drop table if exists testing;
go
create table testing (id int, display varchar(16));
insert into testing values (1, 'Apple');
insert into testing values (2, 'Banana');
insert into testing values (3, 'Celery');
insert into testing values (4, 'Dragonfruit');
insert into testing values (5, 'Eggplant');

DROP FUNCTION IF EXISTS dbo.TranslateVariables
go
CREATE FUNCTION dbo.TranslateVariables
(
    @StringValue VARCHAR(MAX)
)
RETURNS TABLE

AS
RETURN (

--Common Table Expression for Translation
WITH TranslationTable
AS (
    SELECT FindValue = '{' + convert(varchar(5),id) + '}' ,ReplaceValue = display,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
    FROM testing
    )
--Recursive CTE to loop through the TranslationTable and replace FindValue with ReplaceValue
,RecursiveCte as
(
SELECT @StringValue AS StrValue
    ,(
        SELECT count(*)
        FROM TranslationTable
        ) AS cnt

UNION ALL

SELECT replace(StrValue, tt.FindValue, tt.Replacevalue)
    ,cnt - 1
FROM RecursiveCte
JOIN TranslationTable tt
    ON tt.rn = cnt )

SELECT StrValue
    ,cnt
FROM RecursiveCte where cnt = 0
    )
go
Run Code Online (Sandbox Code Playgroud)
--Verify translation
SELECT *
FROM dbo.TranslateVariables('{1} {3}')
OPTION (MAXRECURSION 32767) -- Don't forget to use the maxrecursion option!
Run Code Online (Sandbox Code Playgroud)
 StrValue     | cnt |
|--------------|-----|
| Apple Celery | 0   |
Run Code Online (Sandbox Code Playgroud)
SELECT *
FROM dbo.TranslateVariables('{3},{4},{5}')
OPTION (MAXRECURSION 32767) -- Don't forget to use the maxrecursion option!
Run Code Online (Sandbox Code Playgroud)
| StrValue                    | cnt |
|-----------------------------|-----|
| Celery,Dragonfruit,Eggplant | 0   |
Run Code Online (Sandbox Code Playgroud)