it-swarm.com.de

Schnellste Methode zum Entfernen nicht numerischer Zeichen aus einer VARCHAR in SQL Server

Ich schreibe ein Import-Dienstprogramm, das Telefonnummern als eindeutigen Schlüssel innerhalb des Imports verwendet.

Ich muss überprüfen, ob die Telefonnummer in meiner Datenbank noch nicht vorhanden ist. Das Problem ist, dass die Telefonnummern in der Datenbank Dinge wie Bindestriche, Klammern und möglicherweise andere Dinge enthalten können. Ich habe eine Funktion geschrieben, um diese Dinge zu entfernen. Das Problem ist, dass es slow ist. Mit tausenden von Datensätzen in meiner Datenbank und tausenden von Datensätzen, die gleichzeitig importiert werden können, kann dieser Prozess unannehmbar langsam sein. Ich habe bereits die Telefonnummernspalte zu einem Index gemacht.

Ich habe versucht, das Skript aus diesem Beitrag zu verwenden:
T-SQL-Abgleich (und andere nicht alphanumerische Zeichen)

Aber das hat es nicht beschleunigt.

Gibt es eine schnellere Möglichkeit, nicht numerische Zeichen zu entfernen? Etwas, das gut funktionieren kann, wenn 10.000 bis 100.000 Datensätze miteinander verglichen werden müssen.

Was auch immer getan wird, muss fast ausführen.

Update
In Anbetracht dessen, worauf die Leute geantwortet haben, denke ich, dass ich die Felder bereinigen muss, bevor ich das Import-Dienstprogramm ausführen kann. 

Um die Frage zu beantworten, in was ich das Import-Dienstprogramm schreibe, handelt es sich um eine C # -App. Ich vergleiche jetzt BIGINT mit BIGINT, ohne die DB-Daten ändern zu müssen, und ich habe immer noch einen Performance-Hit mit einem sehr kleinen Datensatz (ca. 2000 Datensätze). 

Könnte der Vergleich von BIGINT mit BIGINT die Dinge verlangsamen?

Ich habe die Codeseite meiner App so weit wie möglich optimiert (entfernte reguläre Ausdrücke, unnötige DB-Aufrufe entfernt). Obwohl ich SQL nicht mehr als Problemquelle isolieren kann, fühle ich mich immer noch so.

59
Dan Herbert

Ich kann es missverstehen, aber Sie haben zwei Datensätze, um die Zeichenfolgen für die aktuellen Daten in der Datenbank zu entfernen, und dann beim Importieren einen neuen Satz.

Um die vorhandenen Datensätze zu aktualisieren, würde ich nur SQL verwenden, das muss nur einmal geschehen.

SQL ist jedoch nicht für diese Art von Vorgang optimiert, da Sie sagten, Sie schreiben ein Import-Dienstprogramm. Ich würde diese Aktualisierungen im Kontext des Import-Dienstprogramms selbst durchführen, nicht in SQL. Dies wäre eine viel bessere Leistung. In was schreibst du das Dienstprogramm?

Ich verstehe den Prozess möglicherweise auch völlig falsch, daher entschuldige ich mich, wenn er nicht in der Basis ist.

Bearbeiten: 
Wenn Sie SQL Server 2005 verwenden, können Sie beim ersten Update eine CLR-Funktion ausprobieren. Hier ist eine kurze Version von Regex. Ich bin mir nicht sicher, wie die Leistung verglichen werden würde. Ich habe es bis auf einen Schnelltest noch nie selbst verwendet.

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, "");  
    }  
};  

Nachdem dies bereitgestellt wurde, können Sie zum Aktualisieren einfach Folgendes verwenden:

UPDATE table SET phoneNumber = dbo.StripNonNumeric(phoneNumber)
15
Scott Nichols

Ich habe diese Lösung mit T-SQL-Code und PATINDEX gesehen. Ich mag das :-)

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
102
David Coster

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,

:)

36
Brainwater

Falls Sie keine Funktion erstellen möchten oder nur einen einzigen Inline-Aufruf in T-SQL benötigen, können Sie Folgendes versuchen:

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

Natürlich ist dies spezifisch für das Entfernen der Telefonnummernformatierung, nicht für ein generisches Entfernen aller Sonderzeichen aus der Zeichenfolgenfunktion.

16
Tom

Einfache funktion:

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
9
AdamE
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('[email protected]#$%^[email protected]%^@#$^')  
6

Das Arbeiten mit Varchars ist aus offensichtlichen Gründen grundsätzlich langsam und ineffizient im Vergleich zum Arbeiten mit Numerik. Die Funktionen, auf die Sie im ursprünglichen Beitrag verweisen, werden in der Tat ziemlich langsam sein, da sie jedes Zeichen in der Zeichenfolge durchlaufen und feststellen, ob es sich um eine Zahl handelt oder nicht. Tun Sie das für tausende von Datensätzen und der Prozess wird zwangsläufig langsam sein. Dies ist der perfekte Job für reguläre Ausdrücke, aber sie werden in SQL Server nicht nativ unterstützt. Sie können mit einer CLR-Funktion Unterstützung hinzufügen, aber es ist schwer zu sagen, wie langsam dies sein wird, ohne es zu versuchen. Ich würde jedoch definitiv erwarten, dass es wesentlich schneller ist, als durch jeden Buchstaben jeder Telefonnummer zu blättern.

Wenn Sie die Telefonnummern in Ihrer Datenbank so formatiert haben, dass sie nur aus Zahlen bestehen, können Sie in SQL zu einem numerischen Typ wechseln, der blitzschnelle Vergleiche mit anderen numerischen Typen liefert. Abhängig davon, wie schnell Ihre neuen Daten eingehen, ist das Trimmen und Konvertieren in numerische Daten auf der Datenbankseite schnell genug, sobald das, was Sie vergleichen, richtig formatiert ist. Wenn möglich, sind Sie jedoch besser Schreiben Sie ein Import-Dienstprogramm in einer .NET-Sprache, das sich um diese Formatierungsprobleme kümmert, bevor Sie auf die Datenbank zugreifen.

In beiden Fällen haben Sie jedoch ein großes Problem hinsichtlich der optionalen Formatierung. Selbst wenn Ihre Nummern in Origin garantiert nur nordamerikanisch sind, wird bei einigen Benutzern die 1 vor einer vollständig für Ortskennzahlen qualifizierten Telefonnummer angezeigt, bei anderen wird dies nicht möglich sein, was zu Mehrfacheingaben derselben Telefonnummer führen kann. Abhängig davon, was Ihre Daten darstellen, verwenden einige Personen ihre private Telefonnummer, unter der möglicherweise mehrere Personen leben, sodass eine einmalige Einschränkung nur ein Datenbankmitglied pro Haushalt zulässt. Einige würden ihre Arbeitsnummer verwenden und das gleiche Problem haben, und andere würden oder würden die Erweiterung einschließen, die wiederum künstliches Eindeutigkeitspotential verursachen würde.

All das kann Sie je nach Ihren Daten und Verwendungen beeinflussen oder auch nicht, aber es ist wichtig, daran zu denken!

1
Grank

Ich würde zuerst Scott's CLR-Funktion ausprobieren, aber eine WHERE-Klausel hinzufügen, um die Anzahl der aktualisierten Datensätze zu reduzieren.

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

Wenn Sie wissen, dass die große Mehrheit Ihrer Datensätze nicht-numerische Zeichen enthält, ist dies möglicherweise nicht hilfreich.

1
Mike L

können Sie sie in einem nächtlichen Prozess entfernen, in einem separaten Feld speichern und geänderte Datensätze aktualisieren, bevor Sie den Prozess ausführen?

Oder speichern Sie beim Einfügen/Aktualisieren das "numerische" Format, um später darauf zu verweisen. Ein Abzug wäre eine einfache Möglichkeit, dies zu tun.

1
Dan Williams

Ich weiß, es ist spät im Spiel, aber hier ist eine Funktion, die ich für T-SQL erstellt habe und nicht numerische Zeichen schnell entfernt. Ich habe ein Schema "String", in das ich Hilfsfunktionen für ...

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

Dann verwenden Sie es zum Vergleichen zum Einfügen.

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
1
Dennis Allen

Ich würde eine Inline-Funktion aus Performance-Sicht verwenden, siehe unten: Beachten Sie, dass Symbole wie '+', '-' usw. nicht entfernt werden

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 */

Sie können es mit mehr als 100 Zeichen definieren ...

0
hkravitz

Tausende von Datensätzen gegen Tausende von Datensätzen sind normalerweise kein Problem. Ich habe SSIS verwendet, um Millionen von Datensätzen zu importieren.

Ich würde die Datenbank bereinigen, um die nicht numerischen Zeichen überhaupt zu entfernen und sie herauszuhalten.

0
Cade Roux

Auf der Suche nach einer sehr einfachen Lösung:

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

"Obwohl ich SQL nicht mehr als Problemquelle isolieren kann, fühle ich mich immer noch so."

Starten Sie den SQL Profiler und werfen Sie einen Blick darauf. Nehmen Sie die resultierenden Abfragen und überprüfen Sie ihre Ausführungspläne, um sicherzustellen, dass der Index verwendet wird.

0
Amy B