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


10

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

SELECT columnA
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'

如何将替换功能集成到select语句中,以便结果集中的所有字符(字母数字,逗号和空格除外)都被替换为''(空格)。这将不起作用:

SELECT replace(columnA,'%[^a-Z0-9, ]%',' ')
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'

Answers:


11

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

但是,如果您可能会发现在美国字母中找不到的字符,但在各种代码页/排序规则中都可以找到用于VARCHAR数据的字符(例如Þ=拉丁大写字母“ Thorn” =SELECT CHAR(0xDE)),那么您可能需要在字符类中包括这些字符:[a-z0-9, Þ]。当然,这些多余的字符将基于每个代码页。

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

LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2

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

DECLARE @Test NVARCHAR(500);
SET @Test = N'this$is%a<>TEST,;to}⌡↕strip╞╟╚══¶out_ç_ƒ▀ special-ij-೫-chars-舛-დ-א-B';
SELECT SQL#.RegEx_Replace4k(@Test, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL); 

返回值:

this is a  TEST, to   strip      out ç ƒ  special ij ೫ chars 舛 დ א B

RegEx表达式的意思是:

  • \W=正则表达式“转义”,表示“任何单词字符”
  • \p{Pc}=“标点符号,连接器”的Unicode“类别”(仅因为\W转义明确排除了此“类别”,才需要匹配)
  • -[,]=类减法(由于逗号包含在\W转义中,因此需要将逗号排除为特殊字符之外的匹配项)

您只需发出以下命令即可更新表:

UPDATE tbl
SET    tbl.field = SQL#.RegEx_Replace4k(tbl.field, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL)
FROM   tbl
WHERE  SQL#.RegEx_IsMatch4k(tbl.field, N'[\W\p{Pc}-[,]]', 1, NULL) = 1;

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

另请参阅:


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);

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

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

就可伸缩性而言,我在30秒内返回了约170k清理的行。再次不确定是否要进行更新,但这是在我的笔记本电脑上,它只有6GB内存,速度相当慢。


0
Declare @String nchar(2000)='hg$%^AB,.:23ab-=+'

Declare @NewString VARCHAR(2000)=''
Declare @Lenght int=LEN(@String)
Declare @Index int=1

WHILE (@Index <= @Lenght)
BEGIN
    Declare @Letter nchar(1)=Substring(@String,@Index,1);
    Declare @ASCII int=ASCII(@Letter);
    If((@ASCII >= 48 and @ASCII <= 57) or (@ASCII >= 97 and @ASCII <= 122) or (@ASCII >= 65 and @ASCII <= 90))
    BEGIN
        SET @NewString += @Letter
    END
    ELSE
    BEGIN
        SET @NewString += ' '
    END
    SET @Index+=1

END
Select @NewString
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.