用空格替换列中的特殊字符

Sta*_*ser 10 sql-server t-sql sql-server-2008-r2 replace

我正在尝试编写一个用空格替换特殊字符的查询。下面的代码有助于识别行。(字母数字字符、逗号和空格有效):

SELECT columnA
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'
Run Code Online (Sandbox Code Playgroud)

如何将替换函数集成到 select 语句中,以便结果集中除字母数字、逗号和空格之外的所有字符都替换为 ' '(空格)。这个不行:

SELECT replace(columnA,'%[^a-Z0-9, ]%',' ')
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 11

如果您保证只使用美国英语字母表的 26 个字母(大写和小写版本),那么当然,您可以使用LIKE和/或PATINDEX使用简单的范围表示法[a-z](您不会使用不区分大小写的排序规则时需要使用大写的“Z”)。

但是,如果您可能会得到在 en-US 字母表中找不到但在各种代码页/VARCHAR数据归类中可用的字符(例如Þ= 拉丁大写字母“Thorn” = SELECT CHAR(0xDE)),那么您可能需要将这些字符包含在字符类中:[a-z0-9, Þ]。当然,这些额外的字符是基于每个代码页的。

此外,请注意排序规则类型(SQL Server 与 Windows)和敏感度设置(大小写、重音等敏感与不敏感)都会影响特定范围内包含的字符。例如,SQL Server 排序规则以与 Windows 排序规则相反的顺序对大写和小写字母进行排序。意思是,假设两种类型的排序规则都区分大小写,一个会做AaBb...,另一个会做aAbB...。效果将是aA-Z其中一个的范围内,而不是另一个。并且 的范围a-Z不会匹配二进制排序规则中的任何字符(以_BIN或结尾_BIN2,但不要使用_BIN),因为 的值A65 并且a是 97,因此它是 97 到 65 之间的无效范围;-)。这里的例子太多了,所以我会尽快在我的博客上发布一个详细的解释(然后会用它的链接更新它)。但是,如果您打算严格只接受美国英语字符(即使您可能会从其他语言获得有效字母),那么您最好的选择可能是使用以下模式排序规则:

LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2
Run Code Online (Sandbox Code Playgroud)

现在,如果您正在支持NVARCHAR数据并且可以从各种语言中获取“单词”字符,那么 T-SQL 将没有多大帮助,因为它没有真正的方法来区分这些东西。在这种情况下,您应该使用正则表达式 (RegEx) —— 特别是Replace方法/函数 —— 而这些只能通过 SQLCLR 获得。下面显示了替换几个“特殊”字符的示例,但保留所有至少一种语言的有效字母:

LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2
Run Code Online (Sandbox Code Playgroud)

返回:

DECLARE @Test NVARCHAR(500);
SET @Test = N'this$is%a<>TEST,;to}??strip?????¶out_ç_ƒ? special-?-?-chars-?-?-?-B';
SELECT SQL#.RegEx_Replace4k(@Test, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL); 
Run Code Online (Sandbox Code Playgroud)

RegEx 表达式的意思是:

  • \W= 一个正则表达式“escape”,意思是“任何单词字符”
  • \p{Pc}=“标点符号,连接符”的 Unicode“类别”(匹配仅需要此“类别”,因为\W转义特别排除了此“类别” )
  • -[,]= 类减法(这需要从匹配中排除逗号作为“特殊”,因为它们包含在\W转义中)

您可以通过发出以下命令来更新表:

this is a  TEST, to   strip      out ç ƒ  special ? ? chars ? ? ? B
Run Code Online (Sandbox Code Playgroud)

请注意,对于这些示例,我使用了我创建的 SQLCLR 函数的免费版SQL#库中提供的两个函数(但同样,这些都是免费的)。另请注意,由于使用NVARCHAR(4000)而不是NVARCHAR(MAX)参数类型,我使用了更快的“4k”版本。如果您的数据正在使用NVARCHAR(MAX),则只需从函数名称中删除“4k”即可。

另请参阅:


Ken*_*her 5

我有一个帖子在这里做了类似的事情

基本上,我使用递归 CTE 一次又一次地循环替换一个“坏”字符。我正在使用 STUFF 去除 1 个字符(尽管您可以用它来替换一个空格)和 PATINDEX 来查找我要删除的字符的位置。你可以稍微修改它来做你正在寻找的东西。然而,它创建了一个“好”列表,它实际上并没有更新现有列表。

DECLARE @Pattern varchar(50) = '%[^A-Za-z0-9, ]%';

WITH FixBadChars AS (SELECT StringToFix, StringToFix AS FixedString, 1 AS MyCounter, Id
                FROM BadStringList
                UNION ALL
                SELECT StringToFix, Stuff(FixedString, PatIndex(@Pattern, 
                    FixedString COLLATE Latin1_General_BIN2), 1, ' ') AS FixedString, 
                    MyCounter + 1, Id
                FROM FixBadChars
                WHERE FixedString COLLATE Latin1_General_BIN2 LIKE @Pattern)
SELECT StringToFix, FixedString, MyCounter, Id
FROM FixBadChars
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);
Run Code Online (Sandbox Code Playgroud)

您应该能够修改底部以进行更新而不仅仅是查询,但我实际上还没有尝试过。我相当确定它看起来像这样:

UPDATE FixBadChars
SET StringToFix = FixedString
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);
Run Code Online (Sandbox Code Playgroud)

至于可扩展性,我在 30 秒内返回了大约 170k 行清理过的行。再次不确定是否进行更新,但这是在我的笔记本电脑上进行的,只有 6GB 内存,速度相当慢。