【问题标题】:Using Wildcard For Range of Characters In T-SQL在 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

标签: sql-server tsql wildcard sql-server-2016


【解决方案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“类别”。对于这种特殊情况,我们只需要使用“字母”类别,其中包括大写、小写和其他形式的字母。单独使用这个类别也会删除连字符和逗号,因为我们可能不想这样做(因为它们在名称中是有效的),我们可以轻松地将它们重新添加。

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

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

    这将我们带到以下示例:

    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
    

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

    在问题中,您设置了 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 个空格。我们可以使用\s{2,} 表示“两个或多个空白字符”或\s\s+ 表示“一个空白字符后跟一个或多个空白字符”的模式。

    例如,如果我们从上一个 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
    

    【讨论】:

      【解决方案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 的组合可能会导致查询的性能很差。
      猜你喜欢
      • 2017-07-17
      • 2020-10-07
      • 1970-01-01
      • 2011-04-03
      • 2023-04-03
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多