对 T-SQL 中的字符范围使用通配符
Using Wildcard For Range of Characters In T-SQL
我目前正在使用 REPLACE
替换客户姓名中可能出现的以下字符。但是,这样做很乏味。
有人知道是否有办法使用列表格式来做到这一点,例如,像这样的通配符:LIKE ['.',',']
而不是每次都写替换?
REPLACE(REPLACE(REPLACE(REPLACE(dname,'.',''),'`',''),'''',''),' ',' ')))
您可以简单地在函数中使用正则表达式来删除或包含您想要的字符。
例如
<pre>Create Function [dbo].[AlphaCharactersOnly](@str VarChar(MAX))
Returns VarChar(MAX)
AS
Begin
Declare @strKeep as varchar(MAX)
Set @strKeep = '%[^ ^a-z]%'
While PatIndex(@strKeep, @str) > 0
Set @str = Stuff(@str, PatIndex(@strKeep, @str), 1, '')
Return @str
End</pre>
我们不知道版本,但如果您只有 2016 年以上,TRANSLATE
可能会在这里工作得很好:
DECLARE @ReplaceChars varchar(50) = '.''`(){}[]!"£$%^&*-=_+';
SELECT REPLACE(REPLACE(TRANSLATE(YourColumn, @ReplaceChars, REPLICATE(LEFT(@ReplaceChars, 1), LEN(@ReplaceChars)),LEFT(@ReplaceChars,1),''),' ',' ')
FROM ...
你仍然需要在最左边的字符上使用 REPLACE
,不过还要使用双空格。
对于这种类型的事情,我会选择 PatExclude8K。它不是标量,100% 基于集合并且速度极快。
要从此字符串中删除非字母数字:
SELECT f.* FROM dbo.PatExclude8K('ABC123!!! ???','[^A-Z0-9]') AS f;
Returns: ABC123
Return 仅来自值 [=29=]:
的数字
DECLARE @table TABLE (someid INT IDENTITY, somestring VARCHAR(100));
INSERT @table (somestring)
SELECT TOP (10) NEWID() FROM sys.all_columns;
SELECT t.someid, t.somestring, pe.NewString
FROM @table AS t
CROSS APPLY dbo.PatExclude8K(t.somestring,'[^0-9]') AS pe
Returns:
someid somestring NewString
----------- ----------------------------------------- ---------------------------
1 2FEF1D43-1A85-456D-BF9E-B329AD64A980 2143185456932964980
2 EB73205F-84C8-407E-8D4F-66FAFD1F556B 7320584840784661556
3 5BEA68B1-783B-4F57-A24D-CF110ADECFEA 568178345724110
4 FC7466E3-5CB8-4DDD-B7F0-30A539DF7C02 746635847030539702
5 800E3AC3-257F-4FF5-B7EE-E6B9268B5608 80033257457692685608
6 A1C33269-48EC-4100-A691-0EA9F2C55E21 1332694841006910925521
7 9C19F844-FE71-40BE-BFFF-276FE344B171 9198447140276344171
8 08529640-E77E-44AD-93A9-E69CE92AF1BD 08529640774493969921
9 FBADC1AE-ED96-4A0E-B106-C6C34E34A612 1964010663434612
10 7E52CFC5-025E-431B-99C1-589E957726B5 75250254319915899577265
如果您使用的是客户名称,那么您确实应该使用 NVARCHAR
而不是 VARCHAR
,因为您不能保证名称只会包含美国英语字符(即 "A" - "Z") 加上一些重音字符(我假设您使用的是默认排序规则 *Latin1_General*
,它又将代码页 1252 用于 VARCHAR
数据)。
也就是说,很多 字符在名称中有效(通常是字母,但也有连字符和逗号),很多 的无效字符。尝试指定任一组,即使是字符 class(即 [...]
)中的一系列字符,每次出现新字符时都可能需要更新。
处理这个问题的一个简单方法是使用正则表达式(即 RegEx,不,LIKE
和 PATINDEX
函数的 [...]
通配符是 not 正则表达式,不管有多少人这样称呼它)。 SQL Server 本身不支持 RegEx,但您可以通过 SQLCLR 获得该功能,它适用于所有本地版本(包括 SQL Server on Linux)从 2005 开始的所有版本,以及 Azure SQL 数据库托管实例;它仅在常规 Azure SQL 数据库和 AWS SQL 服务器 RDS(从 2017 版开始)上不可用。获取 RegEx 的一种简单方法是下载并安装 SQL#,这是我创建的一个 SQLCLR 库(大多数 RegEx 函数都在免费版本中,包括我将在下面使用的内容)。
正则表达式不仅可以处理复杂的模式(比我们在这里处理的要复杂得多),而且它们还允许我们指定 Unicode "categories"。对于这种特殊情况,我们只需要使用 "Letter" 类别,它包括大写、小写和其他形式的字母。单独使用此类别也将删除连字符和逗号,并且由于我们可能不想这样做(因为它们在名称中有效),我们可以轻松地将它们添加回去。
我们将使用的表达式是:[^\p{L}, -]
。此模式读作:
[^...]
= 查找不匹配此列表中的字符的任何单个字符
\p{L}
= 匹配任何归类为 "Letter" 的字符(在 any 语言中,这就是为什么这有效)
, -
= 匹配逗号、space 和连字符。由于连字符在字符 classes 中用于指示范围,因此如果要用作文字连字符,则它们必须是第一个或最后一个字符。
这给我们带来了以下示例:
SELECT SQL#.RegEx_Replace4k(
N'a .` ''b$c d ef-ghi,jr. ꓤ ඖ ל ؼ ញ z', -- string to modify
N'[^\p{L}, -]', -- regular expression (pattern)
N'', -- replacement
-1, -- number of occurrences to replace (-1 = unlimited)
1, -- character position to start at
NULL -- RegEx options (such as case-insensitive, multi-line, etc)
);
--a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z
当然,这仍然给我们留下了一个没有其他答案(正确)解决的问题:将多个 space 转换为单个 space。
在问题中,您设置了一个 REPLACE
来将两个 space 转换为一个 space。只有 只有 两个 space 才有效。如果有三个或更多 spaces,那么它只会转换每两个一组,这仍然会给您留下多个 spaces。例如:
SELECT REPLACE(N'a b', N' ', N' ') AS [3 spaces],
REPLACE(N'a b', N' ', N' ') AS [4 spaces],
REPLACE(N'a b', N' ', N' ') AS [5 spaces];
/*
3 spaces 4 spaces 5 spaces
a b a b a b
*/
如您所见,“3”和“4”space 测试都留下了两个 space,而“5”space 测试留下了三个 spaces.
这是 RegEx 非常适合的另一种操作。您可以指定一个匹配 "two or more spaces" 的模式,然后它将处理任意数量的 space 并将匹配的任何内容替换为单个 space,无论是 2、3 还是 27 space秒。我们可以使用 \s{2,}
表示 "two or more white-space characters" 或 \s\s+
表示 "a white-space character followed by one-or-more white-space characters".
的模式
例如,如果我们从之前的RegEx测试的输出开始,我们可以这样做:
SELECT SQL#.RegEx_Replace4k(
N'a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z', N'\s{2,}',
N' ',
-1, 1, NULL);
--a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z
我目前正在使用 REPLACE
替换客户姓名中可能出现的以下字符。但是,这样做很乏味。
有人知道是否有办法使用列表格式来做到这一点,例如,像这样的通配符:LIKE ['.',',']
而不是每次都写替换?
REPLACE(REPLACE(REPLACE(REPLACE(dname,'.',''),'`',''),'''',''),' ',' ')))
您可以简单地在函数中使用正则表达式来删除或包含您想要的字符。
例如
<pre>Create Function [dbo].[AlphaCharactersOnly](@str VarChar(MAX))
Returns VarChar(MAX)
AS
Begin
Declare @strKeep as varchar(MAX)
Set @strKeep = '%[^ ^a-z]%'
While PatIndex(@strKeep, @str) > 0
Set @str = Stuff(@str, PatIndex(@strKeep, @str), 1, '')
Return @str
End</pre>
我们不知道版本,但如果您只有 2016 年以上,TRANSLATE
可能会在这里工作得很好:
DECLARE @ReplaceChars varchar(50) = '.''`(){}[]!"£$%^&*-=_+';
SELECT REPLACE(REPLACE(TRANSLATE(YourColumn, @ReplaceChars, REPLICATE(LEFT(@ReplaceChars, 1), LEN(@ReplaceChars)),LEFT(@ReplaceChars,1),''),' ',' ')
FROM ...
你仍然需要在最左边的字符上使用 REPLACE
,不过还要使用双空格。
对于这种类型的事情,我会选择 PatExclude8K。它不是标量,100% 基于集合并且速度极快。
要从此字符串中删除非字母数字:
SELECT f.* FROM dbo.PatExclude8K('ABC123!!! ???','[^A-Z0-9]') AS f;
Returns: ABC123
Return 仅来自值 [=29=]:
的数字DECLARE @table TABLE (someid INT IDENTITY, somestring VARCHAR(100));
INSERT @table (somestring)
SELECT TOP (10) NEWID() FROM sys.all_columns;
SELECT t.someid, t.somestring, pe.NewString
FROM @table AS t
CROSS APPLY dbo.PatExclude8K(t.somestring,'[^0-9]') AS pe
Returns:
someid somestring NewString
----------- ----------------------------------------- ---------------------------
1 2FEF1D43-1A85-456D-BF9E-B329AD64A980 2143185456932964980
2 EB73205F-84C8-407E-8D4F-66FAFD1F556B 7320584840784661556
3 5BEA68B1-783B-4F57-A24D-CF110ADECFEA 568178345724110
4 FC7466E3-5CB8-4DDD-B7F0-30A539DF7C02 746635847030539702
5 800E3AC3-257F-4FF5-B7EE-E6B9268B5608 80033257457692685608
6 A1C33269-48EC-4100-A691-0EA9F2C55E21 1332694841006910925521
7 9C19F844-FE71-40BE-BFFF-276FE344B171 9198447140276344171
8 08529640-E77E-44AD-93A9-E69CE92AF1BD 08529640774493969921
9 FBADC1AE-ED96-4A0E-B106-C6C34E34A612 1964010663434612
10 7E52CFC5-025E-431B-99C1-589E957726B5 75250254319915899577265
如果您使用的是客户名称,那么您确实应该使用 NVARCHAR
而不是 VARCHAR
,因为您不能保证名称只会包含美国英语字符(即 "A" - "Z") 加上一些重音字符(我假设您使用的是默认排序规则 *Latin1_General*
,它又将代码页 1252 用于 VARCHAR
数据)。
也就是说,很多 字符在名称中有效(通常是字母,但也有连字符和逗号),很多 的无效字符。尝试指定任一组,即使是字符 class(即 [...]
)中的一系列字符,每次出现新字符时都可能需要更新。
处理这个问题的一个简单方法是使用正则表达式(即 RegEx,不,LIKE
和 PATINDEX
函数的 [...]
通配符是 not 正则表达式,不管有多少人这样称呼它)。 SQL Server 本身不支持 RegEx,但您可以通过 SQLCLR 获得该功能,它适用于所有本地版本(包括 SQL Server on Linux)从 2005 开始的所有版本,以及 Azure SQL 数据库托管实例;它仅在常规 Azure SQL 数据库和 AWS SQL 服务器 RDS(从 2017 版开始)上不可用。获取 RegEx 的一种简单方法是下载并安装 SQL#,这是我创建的一个 SQLCLR 库(大多数 RegEx 函数都在免费版本中,包括我将在下面使用的内容)。
正则表达式不仅可以处理复杂的模式(比我们在这里处理的要复杂得多),而且它们还允许我们指定 Unicode "categories"。对于这种特殊情况,我们只需要使用 "Letter" 类别,它包括大写、小写和其他形式的字母。单独使用此类别也将删除连字符和逗号,并且由于我们可能不想这样做(因为它们在名称中有效),我们可以轻松地将它们添加回去。
我们将使用的表达式是:[^\p{L}, -]
。此模式读作:
[^...]
= 查找不匹配此列表中的字符的任何单个字符\p{L}
= 匹配任何归类为 "Letter" 的字符(在 any 语言中,这就是为什么这有效), -
= 匹配逗号、space 和连字符。由于连字符在字符 classes 中用于指示范围,因此如果要用作文字连字符,则它们必须是第一个或最后一个字符。
这给我们带来了以下示例:
SELECT SQL#.RegEx_Replace4k(
N'a .` ''b$c d ef-ghi,jr. ꓤ ඖ ל ؼ ញ z', -- string to modify
N'[^\p{L}, -]', -- regular expression (pattern)
N'', -- replacement
-1, -- number of occurrences to replace (-1 = unlimited)
1, -- character position to start at
NULL -- RegEx options (such as case-insensitive, multi-line, etc)
);
--a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z
当然,这仍然给我们留下了一个没有其他答案(正确)解决的问题:将多个 space 转换为单个 space。
在问题中,您设置了一个 REPLACE
来将两个 space 转换为一个 space。只有 只有 两个 space 才有效。如果有三个或更多 spaces,那么它只会转换每两个一组,这仍然会给您留下多个 spaces。例如:
SELECT REPLACE(N'a b', N' ', N' ') AS [3 spaces],
REPLACE(N'a b', N' ', N' ') AS [4 spaces],
REPLACE(N'a b', N' ', N' ') AS [5 spaces];
/*
3 spaces 4 spaces 5 spaces
a b a b a b
*/
如您所见,“3”和“4”space 测试都留下了两个 space,而“5”space 测试留下了三个 spaces.
这是 RegEx 非常适合的另一种操作。您可以指定一个匹配 "two or more spaces" 的模式,然后它将处理任意数量的 space 并将匹配的任何内容替换为单个 space,无论是 2、3 还是 27 space秒。我们可以使用 \s{2,}
表示 "two or more white-space characters" 或 \s\s+
表示 "a white-space character followed by one-or-more white-space characters".
例如,如果我们从之前的RegEx测试的输出开始,我们可以这样做:
SELECT SQL#.RegEx_Replace4k(
N'a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z', N'\s{2,}',
N' ',
-1, 1, NULL);
--a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z