在 T-SQL 中对字符范围使用通配符

Posted

技术标签:

【中文标题】在 T-SQL 中对字符范围使用通配符【英文标题】:Using Wildcard For Range of Characters In T-SQL 【发布时间】:2019-08-16 10:09:46 【问题描述】:

我目前正在使用REPLACE 替换以下可能在客户名称中找到的字符。但是,这样做很乏味。

有谁知道是否有办法使用列表格式来做到这一点,例如,像这样的通配符:LIKE ['.',','],而不是每次都写替换?

REPLACE(REPLACE(REPLACE(REPLACE(dname,'.',''),'`',''),'''',''),'  ',' ')))

【问题讨论】:

你正在使用哪个 dbms (mysql, postgresql, tsql / sql-server, oracle)? @SebastianBrosch 我正在使用 T-SQL @user3396351 清理此数据的最佳位置是客户端应用程序,而不是 T-SQL。也就是说,SQL Server 2016 及更高版本允许您使用 R 和 Python 脚本。您可以使用 R 或 Python 脚本通过简单的正则表达式来清理名称,或使用文本处理和数据清理包,如 janitor 什么版本的 SQL Server?如果你是 2016 年,你有 TRANSLATE,这可能会让这更容易。 直到 SQL SERVER 2017 才引入 TRANSLATE 【参考方案1】:

我们不知道版本,但如果你只有 2016 年以上,TRANSLATE 可能在这里工作得很好:

DECLARE @ReplaceChars varchar(50) = '.''`()[]!"£$%^&*-=_+';

SELECT REPLACE(REPLACE(TRANSLATE(YourColumn, @ReplaceChars, REPLICATE(LEFT(@ReplaceChars, 1), LEN(@ReplaceChars)),LEFT(@ReplaceChars,1),''),'  ',' ')
FROM ...

你仍然需要在最左边的字符上使用REPLACE,不过还有两个空格。

【讨论】:

我相信这会起作用,但由于某种原因,它说'TRANSLATE' is not a recognized built-in function name.我在 SSMS 17 上运行 SQL Server 2016,我在网上进行了研究,显然其他人在使用@987654325 时也遇到了同样的问题@在SSMS17上 这不是 SSMS 问题,@user3396351。 SSMS 对查询在数据引擎上的行为方式没有影响;它纯粹是一个交互和表示层。您的数据库处于什么兼容级别?它必须是 130 或更高。 @user3396351 SSMS 只是一个客户端工具。它不会影响数据库中可以使用或不能使用的内容 @Larnu 我当前的兼容级别设置为 110 然后@user3396351 是你的问题。 110 是 SQL Server 2012 级别,TRANSLATE 在那里不起作用。如果您无法更改级别,您将返回嵌套的 REPLACE 语句。【参考方案2】:

对于这种类型的事情,我会选择PatExclude8K。它不是标量的,100% 基于集合的,而且速度极快。

要从此字符串中删除非字母数字:

SELECT f.* FROM dbo.PatExclude8K('ABC123!!!   ???','[^A-Z0-9]') AS f;

返回: ABC123

只返回值表中的数字:

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

返回:

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

【讨论】:

【参考方案3】:

如果您使用客户名称,那么您真的应该使用 NVARCHAR 而不是 VARCHAR,因为您不能保证名称只会包含美国英文字符(即“A”-“Z”)加上一些什锦重音字符(我假设您使用的是默认排序规则 *Latin1_General*,而 VARCHAR 数据又使用代码页 1252)。

也就是说,有 很多 字符在名称中是有效的(通常是字母,但也有连字符和逗号),而 很多 字符是无效的.尝试指定任一组,即使是字符类中的一系列字符(即[...]),每次出现新字符时都可能需要更新。

处理此问题的一种简单方法是使用正则表达式(即 RegEx,不,LIKEPATINDEX 函数的[...] 通配符不是正则表达式,不不管有多少人这样称呼它)。 SQL Server 本身不支持 RegEx,但您可以通过 SQLCLR 获得该功能,该功能适用​​于从 2005 开始的所有版本的所有本地版本(包括 Linux 上的 SQL Server)和 Azure SQL 数据库托管实例;它仅在常规 Azure SQL 数据库和 AWS SQL Server RDS(从 2017 版开始)上不可用。获取 RegEx 的一个简单方法是下载并安装 SQL#,这是我创建的一个 SQLCLR 库(大部分 RegEx 函数都在免费版本中,包括我将在下面使用的)。

正则表达式不仅可以处理复杂的模式(比我们在这里处理的要复杂得多),而且它们还允许我们指定 Unicode“类别”。对于这种特殊情况,我们只需要使用“字母”类别,其中包括大写、小写和其他形式的字母。单独使用这个类别也会删除连字符和逗号,因为我们可能不想这样做(因为它们在名称中是有效的),我们可以轻松地将它们重新添加。

我们将使用的表达式是:[^\pL, -]。这个模式读作:

[^...] = 找到与此列表中的字符匹配的任何单个字符 \pL = 匹配任何归类为“字母”的字符(在任何语言中,这就是为什么这样有效) , - = 匹配逗号、空格和连字符。由于连字符在字符类中用于表示范围,因此如果要用作文字连字符,它们必须是第一个字符或最后一个字符。

这将我们带到以下示例:

SELECT SQL#.RegEx_Replace4k(
            N'a    .`     ''b$c   d  ef-ghi,jr. ꓤ ඖ  ל ؼ ញ z', -- string to modify
            N'[^\pL, -]',   -- 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

当然,这仍然给我们留下了一个没有其他答案(正确)解决的问题:将多个空格转换为一个空格。

在问题中,您设置了 REPLACE 以将两个空格转换为一个空格。这只有在 only 两个空格的情况下才有效。如果有三个或更多空格,那么它只会转换每两个一组,这仍然会给您留下多个空格。例如:

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”空格测试都留下两个空格,而“5”空格测试留下三个空格。

这是 RegEx 非常适合的另一种操作类型。您可以指定一个模式以匹配“两个或多个空格”,然后它将处理任意数量的空格并将匹配的任何内容替换为单个空格,无论是 2、3 还是 27 个空格。我们可以使用\s2, 表示“两个或多个空白字符”或\s\s+ 表示“一个空白字符后跟一个或多个空白字符”的模式。

例如,如果我们从上一个 RegEx 测试的输出开始,我们可以执行以下操作:

SELECT SQL#.RegEx_Replace4k(
           N'a         bc   d  ef-ghi,jr ꓤ ඖ  ל ؼ ញ z', N'\s2,',
           N' ',
           -1, 1, NULL);

--a bc d ef-ghi,jr ꓤ ඖ ל ؼ ញ z

【讨论】:

【参考方案4】:

您可以简单地在函数中使用正则表达式来删除或包含您想要的字符。

例如

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

【讨论】:

标量函数和WHILE 的组合可能会导致查询的性能很差。

以上是关于在 T-SQL 中对字符范围使用通配符的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL 转义select …. like中的特殊字符(百分号)

使用具有相对日期范围和标准 SQL 的 Bigquery Table 通配符 [重复]

SQL Server 之T-SQL基本语句

SQL:一个表多列模糊查询

模糊查询

SQL多表模糊查询