在SQL Server中从VARCHAR中删除非数字字符的最快方法

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

我需要检查电话号码是否已经存在于我的数据库中。 问题是,在数据库中的电话号码可能有像破折号和括号,可能还有其他的东西。 我写了一个函数来删除这些东西,问题是,它是缓慢的,我的数据库中有成千上万的logging,同时导入了数千个logging,这个过程可能会慢得令人无法接受。 我已经把电话号码栏作为索引。

我尝试使用这个post的脚本:
T-SQL trim(和其他非字母数字字符)

但是这并没有加速。

有没有更快的方法来删除非数字字符? 当10,000到100,000条logging必须进行比较时,这个function可以很好地发挥作用。

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

更新
鉴于人们的反应,我想我将不得不清理领域之前,我运行导入实用程序。

为了回答我在编写导入实用程序的问题,它是一个C#应用程序。 现在我正在比较BIGINT和BIGINT,不需要改变DB数据,而且我仍然用非常小的一组数据(约2000条logging)进行性能testing。

可以比较BIGINT和BIGINT会减慢速度吗?

我尽可能地优化了应用程序的代码端(删除了正则expression式,删除了不必要的数据库调用)。 尽pipe我不能将SQL隔离为问题的根源,但我依然觉得这是事实。

我可能会误解,但是有两组数据可以从数据库中删除当前数据的string,然后在导入时删除新的数据集。

为了更新现有的logging,我只是使用SQL,只需要发生一次。

但是,SQL没有针对这种操作进行优化,因为您说您正在编写一个导入实用程序,所以我会在导入实用程序本身而不是SQL中执行这些更新。 这将是更好的性能明智的。 你在写什么工具?

另外,我可能完全误解了这个过程,所以我非常抱歉。

编辑:
对于最初的更新,如果您使用的是SQL Server 2005,则可以尝试CLRfunction。 这是一个快速的使用正则expression式。 不知道如何比较性能,除了现在快速testing,我从来没有使用过这个。

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) 

我用T-SQL代码和PATINDEX看到了这个解决scheme。 我喜欢 :-)

 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 

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

🙂

如果你不想创build一个函数,或者你只需​​要在T-SQL中进行一个内联调用,就可以尝试:

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

当然这是特定于删除电话号码格式,而不是从string函数中删除所有特殊字符。

简单的function:

 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 
 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%^@#$^') 

你可以在夜间过程中删除它们,将它们存储在单独的字段中,然后在运行过程之前对更改的logging进行更新?

或者在插入/更新时,存储“数字”格式,稍后参考。 触发器将是一个简单的方法来做到这一点。

我会先尝试Scott的CLR函数,但是添加一个WHERE子句以减less更新logging的数量。

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

如果你知道绝大多数的logging有非数字字符,它可能没有帮助。

我知道游戏迟到了,但是这里是我为T-SQL创build的一个函数,它可以快速删除非数字字符。 值得注意的是,我有一个模式“string”,我把string的实用函数…

 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 

与使用数字相比,使用variables基本上是缓慢和低效的,原因很明显。 在原始post中链接到的函数确实会很慢,因为它们循环访问string中的每个字符以确定它是否是数字。 这样做的成千上万的logging,这个过程肯定是缓慢的。 对于正则expression式来说,这是一个完美的工作,但在SQL Server中并不支持它们。 你可以使用CLR函数添加支持,但是很难说没有尝试的话会有多慢。但是我肯定会期望它比循环每个电话号码的每个字符要快得多。

一旦获得数据库中格式化的电话号码,以便它们只是数字,则可以在SQL中切换到数字types,这将与其他数字types进行闪电般的比较。 你可能会发现,根据你的新数据进入的速度,在数据库端进行修改和转换为数字的速度足够快,一旦你比较的格式正确,但如果可能的话,你会更好closures在.NET语言中编写导入实用程序,在访问数据库之前会处理这些格式化问题。

无论哪种方式,你将有一个关于可选格式的大问题。 即使你的电话号码只能保证北美地区的电话号码,有些人会把1号码放在一个完全符合区号的电话号码前面,其他人不会这样做,这将会导致同一电话号码有多次input的可能性。 此外,根据您的数据,有些人会使用他们的家庭电话号码,可能有几个人住在那里,所以一个独特的限制,只允许每户一个数据库成员。 有些人会使用他们的工作号码,并有相同的问题,有些人会包括或不会包括将再次造成人为的独特性的延伸。

所有这些都可能会影响到你,这取决于你的具体数据和用途,但是要记住!

我会build议在数据库中强制执行严格的电话号码格式。 我使用以下格式。 (假设美国的电话号码)

数据库:5555555555×555

显示:(555)555-5555转555

input:任何string中embedded10位或更多数字。 (正则expression式replace删除所有非数字字符)

“虽然我不能隔离SQL作为问题的根源,但我依然觉得这是事实。”

启动SQL Profiler并看看。 执行结果查询并检查其执行计划,以确保正在使用索引。

成千上万的logging通常不是一个问题。 我已经使用SSIS来导入数百万条logging,并且像这样去重复。

我会清理数据库,首先删除非数字字符,并将其保留。

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

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

我会从性能angular度使用内联函数,请参阅下面的内容: 请注意,诸如“+”,“ – ”等符号不会被删除

 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个字符来定义它…