#sql #tsql #dynamic #replace
#sql #tsql #динамический #заменить
Вопрос:
Чего я пытаюсь добиться, так это сделать динамической серию замен, которые должны выполняться в определенном поле. (Чтобы сделать все еще проще, я хочу фактически удалить данные, поэтому я всегда буду сравнивать с
Скажите, что иногда мне придется делать только одну замену:
... REPLACE(myField, stringToRemove, '')
Иногда мне понадобятся две замены:
... REPLACE(REPLACE(myField, stringToRemove, ''), anotherStringToRemove, '')
Однако мне нужно сделать это динамичным, и я заранее не знаю, сколько из этих значений у меня будет, и, следовательно, сколько замен (удалений) Я должен буду сделать.
Я попытался выполнить поиск функций обработки агрегированных строк, и, конечно же, их нет. Я также знаю, что это может быть достигнуто с помощью агрегатной функции CLR, но у меня нет возможности ее использовать.
Есть идеи?
Комментарии:
1. Каковы ваши условия для количества
REPLACE()
вызовов?2. Динамическая переменная. Сейчас это количество строк, соответствующих определенному условию. Вы можете представить это как таблицу конфигурации:
Replacements(stringToRemove nvarchar)
Ответ №1:
Вы можете настроить табличную переменную с помощью fromValue и toValue и использовать цикл while для выполнения замен.
-- Table to replace in
declare @T table
(
Value varchar(50)
)
insert into @T values
('first second third'),
('first second third')
-- Table with strings to replace
declare @Rep table
(
ID int identity primary key,
FromValue varchar(50),
ToValue varchar(50)
)
insert into @Rep values
('second', 'fourth'),
('third', 'fifth')
declare @ID int
select @ID = max(ID)
from @Rep
while @ID > 0
begin
update @T
set Value = replace(Value, FromValue, ToValue)
from @Rep
where ID = @ID
set @ID -= 1
end
select *
from @T
Результат:
Value
-------------------
first fourth fifth
first fourth fifth
Если вы хотите запрашивать только значения, вы можете сделать что-то вроде этого.
;with C as
(
select 0 as ID,
Value,
0 as Lvl
from @T
union all
select R.ID,
cast(replace(C.Value, R.FromValue, R.ToValue) as varchar(50)),
Lvl 1
from @Rep as R
inner join C
on C.ID 1 = R.ID
)
select top 1 with ties Value
from C
order by Lvl desc
Комментарии:
1. Я склоняюсь перед вами! Вы не только попадаете в точку, но и делаете так, чтобы это выглядело просто (что обычно является признаком хорошего учителя). Большое спасибо. 🙂
2. @Alpha — Спасибо, я рад, что смог помочь.
Ответ №2:
Как только вы реализуете агрегатную функцию CLR ниже, вы можете сделать:
SELECT dbo.ReplaceAgg(t.[text], w.badword, w.goodword) // call CLR aggregate function
FROM [Texts] t CROSS JOIN BadWords w
GROUP BY t.[text]
Агрегатная функция CLR в C#
/// <summary>
/// Allows to apply regex-replace operations to the same string.
/// For example:
/// SELECT dbo.ReplaceAgg(t.[text], w.badpattern, "...")
/// FROM [Texts] t CROSS JOIN BadPatterns w
/// GROUP BY t.[text]
/// </summary>
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined,
IsInvariantToDuplicates = true, IsInvariantToOrder = false,
IsInvariantToNulls = true, MaxByteSize = -1)]
public class RegexReplaceAgg : IBinarySerialize
{
private string str;
private string needle;
private string replacement;
public void Init()
{
str = null;
needle = null;
replacement = null;
}
public void Accumulate(SqlString haystack, SqlString needle, SqlString replacement)
{
// Null values are excluded from aggregate.
if (needle.IsNull) return;
if (replacement.IsNull) return;
if (haystack.IsNull) return;
str = str ?? haystack.Value;
this.needle = needle.Value;
this.replacement = replacement.Value;
str = Regex.Replace(str, this.needle, this.replacement, RegexOptions.Compiled | RegexOptions.CultureInvariant);
}
public void Merge(RegexReplaceAgg group)
{
Accumulate(group.Terminate(), new SqlString(needle), new SqlString(replacement));
}
public SqlString Terminate() => new SqlString(str);
public void Read(BinaryReader r)
{
str = r.ReadString();
needle = r.ReadString();
replacement = r.ReadString();
}
public void Write(BinaryWriter w)
{
w.Write(str);
w.Write(needle);
w.Write(replacement);
}
}
Ответ №3:
Возможно, вам придется написать скалярную функцию, которой вы передаете исходную строку, и достаточно информации, чтобы она знала, какие строки нужно удалить, и чтобы она перебирала их и возвращала результат набора замен.