替换多行的字符串

Replacing a string over multiple rows

我有一个像这样的table:

ID  Code         CodeType  CodeExt
1   -S_P&S_A     S_P       A+B
1   -S_P&S_A     S_A       C
2   S_P/S_A/S_Z  S_P       A+B+C
2   S_P/S_A/S_Z  S_A       D
2   S_P/S_A/S_Z  S_Z       E
3   S_P          S_P       A

预期输出为

ID  Code_new
1   -(A+B)&(C)
2   (A+B+C)/(D)/(E)
3   (A)

所以我希望根据 CodeType.

将列 Code 替换为 CodeExt

我试过的是

SELECT ID, REPLACE(Code,CodeType,'('+CodeExt+')') AS Code_new
FROM table

但这并没有提供预期的输出。

有趣的问题。我想我有一个您可以尝试的可行解决方案。如果您的样本数据中没有显示额外的分隔符或可能更复杂的示例,您可能需要稍微增加,但它应该为您提供一些可以构建的东西。

首先通过转换为 json 数组(这提供了排序顺序),将 code 拆分为分隔符(/ 或 &)上的单独行。

然后用新值替换 code 和 re-aggregate,为除最后一行以外的所有行添加分隔符。

with n as (
  select ID, Value, seq, Replace(Value,CodeType,Concat('(',CodeExt,')')) NewValue,
    case when seq < Max(seq) over(partition by ID) then
      case when Code like '%&%' then '&' when Code like '%/%' then '/' else '' end
    end sep
  from t
  cross apply (
    select Value, [Key] seq 
    from OpenJson(Concat('["',Replace(Translate(code,'&/', ',,'),',','","'),'"]'))
  )s
)
select ID, String_Agg(Concat(NewValue, sep),'') within group(order by seq) Code_New
from n 
where Value != NewValue
group by ID;

结果:

Working Fiddle Demo

另一种选择是使用递归 CTE。

其工作方式是对每行 Code 进行编号,然后取第一行并进行替换,然后递归取所有行,直到还剩 none。

如果每行替换都编号(按 Code 分区),这样会更有效,因为这样您就可以避免每次 运行 和 row-numbers。

WITH numbered AS (
    SELECT *,
      rn = ROW_NUMBER() OVER (PARTITION BY t.Code ORDER BY t.CodeType),
      IsLast = CASE WHEN LEAD(t.CodeType) OVER (PARTITION BY t.Code ORDER BY t.CodeType) IS NULL THEN 1 END
    FROM t
),
cte AS (
    SELECT
      n.ID,
      n.IsLast,
      Code_new = REPLACE(n.Code, n.CodeType, '(' + n.CodeExt + ')'),
      Level = 1
    FROM numbered n
    WHERE n.rn = 1
    
    UNION ALL
    
    SELECT
      cte.ID,
      n.IsLast,
      REPLACE(cte.Code_new, n.CodeType, '(' + n.CodeExt + ')'),
      cte.Level + 1
    FROM cte
    JOIN numbered n ON n.ID = cte.ID AND n.rn = cte.Level + 1
)
SELECT
  cte.ID,
  cte.Code_new
FROM cte
WHERE cte.IsLast = 1;

db<>fiddle