替换多行的字符串
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;
结果:
另一种选择是使用递归 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;
我有一个像这样的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;
结果:
另一种选择是使用递归 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;