【问题标题】:Aggregate replace in SQL Server?SQL Server 中的聚合替换?
【发布时间】:2011-12-12 06:27:41
【问题描述】:

我想要实现的是动态地进行一系列必须在某个字段上执行的替换。 (为了让事情变得更简单,我实际上想删除数据,所以我会一直比较

说有时我只需要做一个替换:

... REPLACE(myField, stringToRemove, '')

有时,我需要两个替换:

... REPLACE(REPLACE(myField, stringToRemove, ''), anotherStringToRemove, '')

但是,我需要使这个动态化,并且我事先不知道我将拥有多少这些值,因此,我必须做多少替换(删除)。

我尝试搜索聚合字符串操作函数,当然,没有。我也知道这可以通过 CLR 聚合函数来实现,但我没有使用它的可能性。

有什么想法吗?

【问题讨论】:

  • REPLACE() 的调用量是什么条件?
  • 动态变量。现在它是匹配某个条件的行数。你可以把它想象成一个配置表:Replacements(stringToRemove nvarchar)

标签: sql tsql dynamic replace


【解决方案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

【讨论】:

  • 我向你鞠躬!不仅你击中了钉子,而且你让它看起来很简单(这通常是一个好老师的标志)。非常感谢。 :)
  • @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]

C# 中的 CLR 聚合函数

/// <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】:

    您可能必须编写一个标量函数,将原始字符串传递给它,并提供足够的信息让它知道要删除哪些字符串,并让它循环遍历它们并返回一组替换的结果。

    【讨论】:

      猜你喜欢
      • 2022-12-11
      • 2010-09-26
      • 1970-01-01
      • 1970-01-01
      • 2015-10-16
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多