it-swarm-eu.dev

Nejrychlejší způsob odstraňování nečíselných znaků z VARCHAR v SQL Serveru

Píšu import nástroj, který používá telefonní čísla jako jedinečný klíč v rámci importu.

Musím zkontrolovat, zda telefonní číslo v mé databázi ještě neexistuje. Problém je v tom, že telefonní čísla v DB mohou mít věci jako pomlčky a závorky a případně další věci. Napsal jsem funkci, abych tyto věci odstranil, problém je, že je pomalý as tisíci záznamy v mé databázi a tisíci záznamů pro import najednou, tento proces může být nepřijatelně pomalý. Sloupec telefonního čísla jsem již vytvořil jako index.

Pokusil jsem se použít skript z tohoto příspěvku:
T-SQL trim & nbsp (a další nealfanumerické znaky)

Ale to nic nezrychlilo.

Existuje rychlejší způsob, jak odstranit nečíselné znaky? Něco, co může fungovat dobře, když je třeba porovnat 10 000 až 100 000 záznamů.

Co se děje, je třeba provést fast.

Aktualizace
Vzhledem k tomu, co lidé odpověděli, myslím, že budu muset vyčistit pole před spuštěním nástroje pro import. 

Chcete-li odpovědět na otázku, co píšu nástroj pro import, jedná se o aplikaci C #. Srovnávám BIGINT s BIGINTem nyní, bez nutnosti měnit data DB a stále s výkonným hitem s velmi malou sadou dat (asi 2000 záznamů). 

Mohlo by srovnání BIGINT s BIGINT zpomalit věci?

Optimalizoval jsem kódovou stránku své aplikace tak, jak jsem mohl (odstranil jsem regexes, odstranil jsem zbytečné DB volání). I když už nemůžu izolovat SQL jako zdroj problému, stále mám pocit, že je.

59
Dan Herbert

Možná jsem nepochopil, ale máte dvě sady dat pro odstranění řetězců z jednoho pro aktuální data v databázi a pak nový soubor při každém importu.

Pro aktualizaci stávajících záznamů bych použil pouze SQL, který se musí stát pouze jednou.

SQL však není pro tento druh operací optimalizován, protože jste říkali, že píšete nástroj pro import, takže bych tyto aktualizace prováděl v rámci samotného nástroje pro import, nikoli v SQL. To by bylo mnohem lepší. Co píšete nástroj?

Také mohu být zcela nepochopením procesu, takže se omlouvám, pokud je to mimo provoz.

Upravit: 
Pro počáteční aktualizaci, pokud používáte SQL Server 2005, můžete zkusit funkci CLR. Zde je rychlý pomocí regexu. Nejsem si jistý, jak bude výkon srovnávat, nikdy jsem to nepoužil sám s výjimkou rychlého testu právě teď.

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

Po nasazení můžete aktualizovat a použít pouze:

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

Toto řešení jsem viděl s kódem T-SQL a PATINDEX. Líbí se mi to :-)

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

V případě, že jste nechtěli vytvořit funkci, nebo jste potřebovali pouze jedno inline volání v T-SQL, můžete zkusit:

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

Samozřejmě to je specifické pro odstranění formátování telefonního čísla, ne obecný odstranit všechny speciální znaky z funkce řetězce.

16
Tom

Jednoduchá funkce:

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
Debayan Samaddar

Práce s varchars je v zásadě pomalá a neefektivní ve srovnání s prací s numerickými znaky, a to ze zřejmých důvodů. Funkce, na které odkazujete v původním příspěvku, budou skutečně poměrně pomalé, protože budou procházet každým znakem v řetězci, aby určily, zda je to číslo. Udělejte to pro tisíce záznamů a proces musí být pomalý. To je ideální úloha pro regulární výrazy, ale nejsou nativně podporovány v SQL Serveru. Můžete přidat podporu pomocí funkce CLR, ale je těžké říct, jak pomalé to bude, aniž by se to snažil bych určitě očekával, že bude výrazně rychlejší než smyčka přes každý znak každého telefonního čísla, nicméně!

Jakmile dostanete telefonní čísla formátovaná v databázi tak, že jsou to pouze čísla, můžete přepnout na numerický typ v SQL, který by přinesl srovnání blesku s jinými číselnými typy. Možná zjistíte, že v závislosti na tom, jak rychle vaše nová data přicházejí, dělá se ořezávání a konverze na numerickou stránku na straně databáze dostatečně rychle, jakmile to, co porovnáváte, je správně formátováno, ale pokud je to možné, budete lepší. vypnout nástroj pro import v jazyce .NET, který by se postaral o tyto problémy s formátováním před tím, než zasáhne databázi.

Ať tak či onak, budete mít velký problém, pokud jde o volitelné formátování. I když je zaručeno, že vaše čísla budou v Severním Americe pouze v Severoamerickém původu, někteří lidé vloží číslo 1 před plně kvalifikované telefonní číslo s kódem oblasti a další nebudou, což způsobí možnost více položek stejného telefonního čísla. Kromě toho, v závislosti na tom, co vaše data představují, někteří lidé budou používat své domovské telefonní číslo, které by mohlo mít několik lidí, kteří tam žijí, takže jedinečné omezení na to by umožnilo pouze jeden člen databáze na domácnost. Někteří by použili své pracovní číslo a měli stejný problém, a někteří by zahrnuli nebo nechtěli zahrnout rozšíření, které by opět způsobilo potenciál umělé jedinečnosti.

To vše může, ale nemusí mít dopad na vás, v závislosti na konkrétních datech a zvyklostech, ale je důležité mít na paměti!

1
Grank

Zkoušel bych nejprve Scottovu CLR funkci, ale přidal klauzuli WHERE, aby se snížil počet aktualizovaných záznamů.

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

Pokud víte, že velká většina vašich záznamů má nečíselné znaky, nemusí to pomoci.

1
Mike L

můžete je odstranit v nočním procesu, uložit je do samostatného pole a provést aktualizaci na změněných záznamech těsně před spuštěním procesu?

Nebo na insert/update, uložit "numerické" formát, odkazovat později. Spouštěč by byl snadný způsob, jak to udělat.

1
Dan Williams

Vím, že je pozdě na hru, ale tady je funkce, kterou jsem vytvořil pro T-SQL, která rychle odstraňuje nečíselné znaky. Všimněte si, že mám schéma "String", které jsem dal utility funkce pro řetězce do ...

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

Pak jej použít k porovnání pro vkládání, něco takového;

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

Použil bych funkci Inline z pohledu výkonu, viz níže: Všimněte si, že symboly jako '+', '-' atd. Nebudou odstraněny

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

Můžete definovat více než 100 znaků ...

0
hkravitz

Tisíce záznamů proti tisícům záznamů obvykle není problém. Použil jsem SSIS k importu milionů záznamů s de-duping takto.

Chtěl bych vyčistit databázi, abych odstranil nečíselné znaky na prvním místě a udržet je.

0
Cade Roux

Hledáte super jednoduché řešení:

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

"I když už nemůžu izolovat SQL jako zdroj problému, stále mám pocit, že je."

Spusťte SQL Profiler a podívejte se. Vezměte výsledné dotazy a zkontrolujte jejich prováděcí plány, abyste se ujistili, že se používá index.

0
Amy B