【问题标题】:Fastest way to remove non-numeric characters from a VARCHAR in SQL Server从 SQL Server 中的 VARCHAR 中删除非数字字符的最快方法
【发布时间】:2010-09-11 11:54:18
【问题描述】:

我正在编写一个导入实用程序,它使用电话号码作为导入中的唯一键。

我需要检查我的数据库中是否不存在该电话号码。问题是数据库中的电话号码可能有破折号和括号之类的东西,可能还有其他东西。我写了一个函数来删除这些东西,问题是它,并且我的数据库中有数千条记录并且一次导入数千条记录,这个过程可能会慢得令人无法接受。我已经将电话号码列设为索引。

我尝试使用此帖子中的脚本:
T-SQL trim &nbsp (and other non-alphanumeric characters)

但这并没有加快速度。

有没有更快的方法来删除非数字字符?当必须比较 10,000 到 100,000 条记录时可以表现良好的东西。

无论做什么都需要快速执行。

更新
鉴于人们的反应,我认为我必须在运行导入实用程序之前清理字段。

要回答我正在编写导入实用程序的问题,它是一个 C# 应用程序。我现在正在将 BIGINT 与 BIGINT 进行比较,无需更改数据库数据,而且我仍在使用非常小的数据集(大约 2000 条记录)对性能造成影响。

将 BIGINT 与 BIGINT 进行比较会减慢速度吗?

我已尽我所能优化了我的应用程序的代码部分(删除了正则表达式,删除了不必要的数据库调用)。虽然我不能再将 SQL 隔离为问题的根源,但我仍然觉得它是。

【问题讨论】:

    标签: sql sql-server performance optimization


    【解决方案1】:

    您能否在每晚的过程中删除它们,将它们存储在单独的字段中,然后在运行该过程之前对更改的记录进行更新?

    或者在插入/更新时,存储“数字”格式,以供以后参考。触发器将是一种简单的方法。

    【讨论】:

      【解决方案2】:

      我可能会误解,但是您有两组数据,用于从数据库中的当前数据中删除字符串,然后在导入时删除一组新数据。

      为了更新现有记录,我只需要使用 SQL,只需执行一次。

      但是,SQL 并未针对此类操作进行优化,因为您说您正在编写导入实用程序,所以我将在导入实用程序本身的上下文中进行这些更新,而不是在 SQL 中。这将是更好的性能明智。你用什么写的实用程序?

      另外,我可能完全误解了这个过程,所以如果偏离基地,我深表歉意。

      编辑:
      对于初始更新,如果您使用的是 SQL Server 2005,您可以尝试使用 CLR 函数。这是一个使用正则表达式的快速方法。不知道性能会如何比较,我自己从来没有使用过这个,除了现在快速测试。

      using System;  
      using System.Data;  
      using System.Text.RegularExpressions;  
      using System.Data.SqlClient;  
      using System.Data.SqlTypes;  
      using Microsoft.SqlServer.Server;  
      
      public partial class UserDefinedFunctions  
      {  
          [Microsoft.SqlServer.Server.SqlFunction]  
          public static SqlString StripNonNumeric(SqlString input)  
          {  
              Regex regEx = new Regex(@"\D");  
              return regEx.Replace(input.Value, "");  
          }  
      };  
      

      部署后,您可以使用以下方式进行更新:

      UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber)
      

      【讨论】:

        【解决方案3】:

        我建议对数据库中的电话号码实施严格的格式。我使用以下格式。 (假设美国电话号码)

        数据库:5555555555x555

        显示:(555) 555-5555 转 555

        输入:任何字符串中嵌入的 10 位或更多位。 (正则表达式替换删除所有非数字字符)

        【讨论】:

          【解决方案4】:

          与使用数字相比,使用 varchars 从根本上说是缓慢且低效的,原因很明显。您在原始帖子中链接到的函数确实会很慢,因为它们循环遍历字符串中的每个字符以确定它是否是数字。对数千条记录这样做,这个过程肯定会很慢。这是正则表达式的完美工作,但 SQL Server 本身并不支持它们。您可以使用 CLR 函数添加支持,但如果不尝试,很难说这会有多慢。不过,我绝对希望它比遍历每个电话号码的每个字符要快得多!

          一旦您在数据库中将电话号码格式化为仅是数字,您就可以在 SQL 中切换到数字类型,这将产生与其他数字类型的闪电般快速的比较。您可能会发现,根据新数据的输入速度,一旦您要比较的数据格式正确,在数据库端进行修剪和转换为数字就足够快了,但如果可能的话,您会更好用 .NET 语言编写一个导入实用程序,在访问数据库之前处理这些格式问题。

          不过,无论哪种方式,您都会遇到关于可选格式的大问题。即使您的号码保证仅来自北美,有些人会将 1 放在一个完全符合区号的电话号码前面,而另一些人则不会,这将导致同一电话号码的多次输入的可能性。此外,根据您的数据所代表的内容,有些人将使用他们的家庭电话号码,其中可能有几个人住在那里,因此对其的唯一约束将只允许每个家庭一个数据库成员。有些人会使用他们的工作编号并遇到同样的问题,有些人会或不会包含会再次导致人为唯一性潜力的扩展名。

          所有这些可能会或可能不会影响您,具体取决于您的特定数据和使用情况,但请务必牢记!

          【讨论】:

            【解决方案5】:

            我会先尝试 Scott 的 CLR 函数,但添加一个 WHERE 子句以减少更新的记录数。

            UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber) 
            WHERE phonenumber like '%[^0-9]%'
            

            如果您知道绝大多数记录都包含非数​​字字符,那么它可能无济于事。

            【讨论】:

              【解决方案6】:

              “虽然我不能再将 SQL 隔离为问题的根源,但我仍然觉得它是。”

              启动 SQL Profiler 并查看一下。获取生成的查询并检查其执行计划以确保正在使用该索引。

              【讨论】:

                【解决方案7】:

                数千条记录对数千条记录通常不是问题。我已经使用 SSIS 导入了数百万条这样的重复数据删除记录。

                我会首先清理数据库以删除非数字字符并将它们排除在外。

                【讨论】:

                  【解决方案8】:

                  我知道游戏已经晚了,但这是我为 T-SQL 创建的一个函数,它可以快速删除非数字字符。值得注意的是,我有一个模式“字符串”,我将字符串的实用函数放入...

                  CREATE FUNCTION String.ComparablePhone( @string nvarchar(32) ) RETURNS bigint AS
                  BEGIN
                      DECLARE @out bigint;
                  
                  -- 1. table of unique characters to be kept
                      DECLARE @keepers table ( chr nchar(1) not null primary key );
                      INSERT INTO @keepers ( chr ) VALUES (N'0'),(N'1'),(N'2'),(N'3'),(N'4'),(N'5'),(N'6'),(N'7'),(N'8'),(N'9');
                  
                  -- 2. Identify the characters in the string to remove
                      WITH found ( id, position ) AS
                      (
                          SELECT 
                              ROW_NUMBER() OVER (ORDER BY (n1+n10) DESC), -- since we are using stuff, for the position to continue to be accurate, start from the greatest position and work towards the smallest
                              (n1+n10)
                          FROM 
                              (SELECT 0 AS n1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) AS d1,
                              (SELECT 0 AS n10 UNION SELECT 10 UNION SELECT 20 UNION SELECT 30) AS d10
                          WHERE
                              (n1+n10) BETWEEN 1 AND len(@string)
                              AND substring(@string, (n1+n10), 1) NOT IN (SELECT chr FROM @keepers)
                      )
                  -- 3. Use stuff to snuff out the identified characters
                      SELECT 
                          @string = stuff( @string, position, 1, '' )
                      FROM 
                          found
                      ORDER BY
                          id ASC; -- important to process the removals in order, see ROW_NUMBER() above
                  
                  -- 4. Try and convert the results to a bigint   
                      IF len(@string) = 0
                          RETURN NULL; -- an empty string converts to 0
                  
                      RETURN convert(bigint,@string); 
                  END
                  

                  然后用它来比较插入,像这样;

                  INSERT INTO Contacts ( phone, first_name, last_name )
                  SELECT i.phone, i.first_name, i.last_name
                  FROM Imported AS i
                  LEFT JOIN Contacts AS c ON String.ComparablePhone(c.phone) = String.ComparablePhone(i.phone)
                  WHERE c.phone IS NULL -- Exclude those that already exist
                  

                  【讨论】:

                    【解决方案9】:
                    create function dbo.RemoveNonNumericChar(@str varchar(500))  
                    returns varchar(500)  
                    begin  
                    declare @startingIndex int  
                    set @startingIndex=0  
                    while 1=1  
                    begin  
                        set @startingIndex= patindex('%[^0-9]%',@str)  
                        if @startingIndex <> 0  
                        begin  
                            set @str = replace(@str,substring(@str,@startingIndex,1),'')  
                        end  
                        else    break;   
                    end  
                    return @str  
                    end
                    
                    go  
                    
                    select dbo.RemoveNonNumericChar('aisdfhoiqwei352345234@#$%^$@345345%^@#$^')  
                    

                    【讨论】:

                      【解决方案10】:

                      寻找一个超级简单的解决方案:

                      SUBSTRING([Phone], CHARINDEX('(', [Phone], 1)+1, 3)
                             + SUBSTRING([Phone], CHARINDEX(')', [Phone], 1)+1, 3)
                             + SUBSTRING([Phone], CHARINDEX('-', [Phone], 1)+1, 4) AS Phone
                      

                      【讨论】:

                        【解决方案11】:

                        我看到了这个使用 T-SQL 代码和 PATINDEX 的解决方案。我喜欢它:-)

                        CREATE Function [fnRemoveNonNumericCharacters](@strText VARCHAR(1000))
                        RETURNS VARCHAR(1000)
                        AS
                        BEGIN
                            WHILE PATINDEX('%[^0-9]%', @strText) > 0
                            BEGIN
                                SET @strText = STUFF(@strText, PATINDEX('%[^0-9]%', @strText), 1, '')
                            END
                            RETURN @strText
                        END
                        

                        【讨论】:

                          【解决方案12】:

                          如果您不想创建函数,或者只需要在 T-SQL 中进行一次内联调用,您可以尝试:

                          set @Phone = REPLACE(REPLACE(REPLACE(REPLACE(@Phone,'(',''),' ',''),'-',''),')','')
                          

                          当然这是专门针对删除电话号码格式的,而不是通用的从字符串函数中删除所有特殊字符。

                          【讨论】:

                            【解决方案13】:

                            replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(string,'a',''),'b',''),'c',''),'d',''),'e',''),'f',''),'g',''),'h',''),'i',''),'j',''),'k',''),'l',''),'m',''),'n',''),'o',''),'p',''),'q',''),'r',''),'s',''),'t',''),'u',''),'v',''),'w',''),'x',''),'y',''),'z',''),'A',''),'B',''),'C',''),'D',''),'E',''),'F',''),'G',''),'H',''),'I',''),'J',''),'K',''),'L',''),'M',''),'N',''),'O',''),'P',''),'Q',''),'R',''),'S',''),'T',''),'U',''),'V',''),'W',''),'X',''),'Y',''),'Z','')*1 AS string,

                            :)

                            【讨论】:

                            • 你忘了 ( ) - # 等等
                            • 爱它!这就是为什么我既喜欢又讨厌 SQL 语言的原因。
                            【解决方案14】:

                            简单功能:

                            CREATE FUNCTION [dbo].[RemoveAlphaCharacters](@InputString VARCHAR(1000))
                            RETURNS VARCHAR(1000)
                            AS
                            BEGIN
                              WHILE PATINDEX('%[^0-9]%',@InputString)>0
                                    SET @InputString = STUFF(@InputString,PATINDEX('%[^0-9]%',@InputString),1,'')     
                              RETURN @InputString
                            END
                            
                            GO
                            

                            【讨论】:

                              【解决方案15】:

                              从性能的角度来看,我会使用内联函数,见下文: 请注意,'+'、'-' 等符号不会被删除

                              CREATE FUNCTION [dbo].[UDF_RemoveNumericStringsFromString]
                               (
                               @str varchar(100)
                               )
                               RETURNS TABLE AS RETURN
                               WITH Tally (n) as 
                                (
                                -- 100 rows
                                 SELECT TOP (Len(@Str)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
                                 FROM (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
                                 CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
                                )
                              
                                SELECT OutStr =  STUFF(
                                     (SELECT SUBSTRING(@Str, n,1) st
                                      FROM Tally
                                      WHERE ISNUMERIC(SUBSTRING(@Str, n,1)) = 1
                                      FOR XML PATH(''),type).value('.', 'varchar(100)'),1,0,'')
                                GO
                              
                                /*Use it*/
                                SELECT OutStr
                                FROM dbo.UDF_RemoveNumericStringsFromString('fjkfhk759734977fwe9794t23')
                                /*Result set
                                 759734977979423 */
                              

                              你可以用超过 100 个字符来定义它...

                              【讨论】:

                              • 你能解释一下为什么'+'和'-'不会被删除吗?编辑:没关系,这是因为 IsNumeric 函数。我用这个替换了 IsNumeric 条件:SUBSTRING(@Str, n,1) BETWEEN '0' AND '9'
                              猜你喜欢
                              • 1970-01-01
                              • 2020-06-09
                              • 2017-05-19
                              • 2022-01-20
                              • 2012-04-25
                              • 2013-08-27
                              • 2011-03-14
                              • 2021-04-19
                              相关资源
                              最近更新 更多