65.9K
CodeProject 正在变化。 阅读更多。
Home

用于在 SQL Server 中替换字符的小函数

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (5投票s)

2024 年 5 月 13 日

CPOL

3分钟阅读

viewsIcon

18048

downloadIcon

288

一个使用 CTE 从字符串中替换不需要的字符的小函数

引言

本技巧展示了如何构建一个小的 SQL Server 函数,该函数利用一个公共表表达式 (CTE) 从输入字符串中替换不需要的字符。虽然这个函数可以在 SELECT 语句中使用,但它也可以用于例如 UPDATE 语句中以清理数据,甚至可以在 WHERE 子句中使用。当用作 WHERE 子句的一部分时,请记住,性能可能会急剧下降,就像在用作条件的任何函数一样。

同样的功能可以通过许多不同的方式实现,例如使用正则表达式、T-SQL 循环等,所以这只是一种实现方式。

使用输入数据

所以目标是创建上面描述的函数。让我们将任务分解成几个部分。首先我们需要做的是,使用输入字符串和要替换的字符字符串,作为 SELECT 语句中的数据。这很容易做到,因为变量可以用作列,而无需查询中的任何表。考虑以下示例

BEGIN
   DECLARE @stringToReplace nvarchar(MAX) = 'this% contains ! illegal/ chars',
           @charsToReplace nvarchar(100)  = '!"#¤%&/()=?',
           @replacement nvarchar(1)       = '';

         SELECT @stringToReplace, @charsToReplace
END;

这会在一行中很好地返回我们的数据

(No column name)                  (No column name)
this% contains ! illegal/ chars   !"#¤%&/()=?

为了简单起见,我稍后在本技巧中在它们自己的查询中使用这两个变量。

创建 CTE

这部分是本技巧的核心内容。让我们看一下整个 CTE 语句

BEGIN
   DECLARE @stringToReplace nvarchar(MAX) = 'this% contains ! illegal/ chars',
           @charsToReplace nvarchar(100)  = '!"#¤%&/()=?',
           @replacement nvarchar(1)       = '';

   WITH
      CharsToReplace (Chars) AS (
         SELECT @charsToReplace
      ),
      InputData (InputString) AS (
         SELECT @stringToReplace
      ),
      ReplaceLoop (Position, SingleChar, OutputString) AS (
         SELECT 1                                AS Position,
                SUBSTRING(ctr.Chars, 1, 1)       AS SingleChar,
                REPLACE(id.InputString,
                     SUBSTRING(ctr.Chars, 1, 1),
                     @replacement)               AS OutputString
         FROM        CharsToReplace ctr
         CROSS APPLY InputData      id
         UNION ALL
         SELECT rl.Position + 1                                AS Position,
                SUBSTRING(ctr.chars, rl.position + 1, 1)       AS SingleChar,
                REPLACE(rl.OutputString,
                     SUBSTRING(ctr.chars, rl.Position + 1, 1),
                     @replacement)                             AS OutputString
         FROM        CharsToReplace ctr
      CROSS APPLY ReplaceLoop    rl
         WHERE LEN(ctr.Chars) > rl.Position
      )
   SELECT *
   FROM ReplaceLoop rl
END;

我假设您已经熟悉公共表表达式,尤其是递归 CTE。如果不是,请查看 WITH common_table_expression (Transact-SQL)

所以前两个 CTE 表只是为了定义我们输入的单独表。替换循环是一个递归查询,循环遍历所有需要替换的字符。锚定语句提取第一个要替换的字符,并使用所需的字符进行替换。替换的结果将由后续迭代使用,因为每次迭代都会提取下一个要替换的字符,并为其生成自己的输出以供下一次迭代使用。

所以如果执行此语句,输出将是

Position   SingleChar   OutputString
1          !            this% contains  illegal/ chars
2          "            this% contains  illegal/ chars
3          #            this% contains  illegal/ chars
4          ¤            this% contains  illegal/ chars
5          %            this contains  illegal/ chars
6          &            this contains  illegal/ chars
7          /            this contains  illegal chars
8          (            this contains  illegal chars
9          )            this contains  illegal chars
10         =            this contains  illegal chars
11         ?            this contains  illegal chars

所以所有的辛苦工作都是由查询完成的。

选择所需的结果

我们绝对不想获取查询返回的所有数据,只有在所有替换完成后,最终结果才有趣。为了实现这一点,可以使用 position 列。如果我们简单地根据位置对结果数据进行降序排序并返回第一行(最高位置),我们就会得到最终结果。

考虑以下内容

BEGIN
   DECLARE @stringToReplace nvarchar(MAX) = 'this% contains ! illegal/ chars',
           @charsToReplace nvarchar(100)  = '!"#¤%&/()=?',
           @replacement nvarchar(1)       = '';

   WITH
      CharsToReplace (Chars) AS (
         SELECT @charsToReplace
      ),
      InputData (InputString) AS (
         SELECT @stringToReplace
      ),
      ReplaceLoop (Position, SingleChar, OutputString) AS (
         SELECT 1                                AS Position,
                SUBSTRING(ctr.Chars, 1, 1)       AS SingleChar,
                REPLACE(id.InputString,
                     SUBSTRING(ctr.Chars, 1, 1),
                     @replacement)               AS OutputString
         FROM        CharsToReplace ctr
         CROSS APPLY InputData      id
         UNION ALL
         SELECT rl.Position + 1                                AS Position,
                SUBSTRING(ctr.chars, rl.position + 1, 1)       AS SingleChar,
                REPLACE(rl.OutputString,
                     SUBSTRING(ctr.chars, rl.Position + 1, 1),
                     @replacement)                             AS OutputString
         FROM        CharsToReplace ctr
      CROSS APPLY ReplaceLoop    rl
         WHERE LEN(ctr.Chars) > rl.Position
      )
   SELECT *
   FROM ReplaceLoop rl
   ORDER BY rl.position DESC
   OFFSET 0 ROWS
   FETCH FIRST 1 ROWS ONLY;
END;

使用 ORDER BY 子句后,结果是

Position   SingleChar   OutputString
11         ?            this contains  illegal chars

现在单行输出正是我们想要的。

函数的实现

最后一件事是将其包装成一个函数。基本上唯一要做的事情是

  • 使用参数定义函数声明
  • 查询所需的数据并返回它

整个函数看起来像这样

CREATE FUNCTION ReplaceChars(@stringToReplace nvarchar(MAX),
                             @charsToReplace nvarchar(100),
                             @replacement nvarchar(1))
RETURNS nvarchar(MAX) AS
BEGIN
   DECLARE @returnData  nvarchar(MAX);

   WITH
      CharsToReplace (Chars) AS (
         SELECT @charsToReplace
      ),
      InputData (InputString) AS (
         SELECT @stringToReplace
      ),
      ReplaceLoop (Position, SingleChar, OutputString) AS (
         SELECT 1                                AS Position,
                SUBSTRING(ctr.Chars, 1, 1)       AS SingleChar,
                REPLACE(id.InputString,
                     SUBSTRING(ctr.Chars, 1, 1),
                     @replacement)               AS OutputString
         FROM        CharsToReplace ctr
         CROSS APPLY InputData      id
         UNION ALL
         SELECT rl.Position + 1                                AS Position,
                SUBSTRING(ctr.chars, rl.position + 1, 1)       AS SingleChar,
                REPLACE(rl.OutputString,
                     SUBSTRING(ctr.chars, rl.Position + 1, 1),
                     @replacement)                             AS OutputString
         FROM        CharsToReplace ctr
         CROSS APPLY ReplaceLoop    rl
         WHERE LEN(ctr.Chars) > rl.Position
      )
   SELECT @returnData = rl.OutputString
   FROM ReplaceLoop rl
   ORDER BY rl.position DESC
   OFFSET 0 ROWS
   FETCH FIRST 1 ROWS ONLY;

   RETURN (@returnData);
END;

让我们试一试。如果我们使用与之前相同的输入,查询将如下所示

SELECT dbo.ReplaceChars('this% contains ! illegal/ chars', '!"#¤%&/()=?', '')

结果将是

(No column name)
this contains  illegal chars

正如开头所说,此函数可以在很多地方使用。例如,如果您想在 UPDATE 语句中使用它来清理数据,查询可能如下所示

UPDATE MyTable
SET MyColumn = dbo.ReplaceChars(MyColumn, '!"#¤%&/()=?', '');

历史

  • 2024 年 5 月 13 日:创建。
© . All rights reserved.