web-dev-qa-db-ja.com

SQL ServerのVARCHARから非数値文字を削除する最も速い方法

インポート内で一意のキーとして電話番号を使用するインポートユーティリティを作成しています。

電話番号がデータベースにまだ存在していないことを確認する必要があります。問題は、DB内の電話番号にダッシュや括弧などが含まれている可能性があることです。これらを削除する関数を作成しましたが、問題はslowで、DBに数千のレコードがあり、一度にインポートする数千のレコードがあることです、このプロセスは許容できないほど遅くなる可能性があります。電話番号の列を既にインデックスにしました。

この投稿のスクリプトを使用してみました:
T-SQL trim&nbsp(およびその他の非英数字)

しかし、それはそれをスピードアップしませんでした。

数字以外の文字を削除するより速い方法はありますか? 10,000から100,000のレコードを比較する必要がある場合にうまく機能する何か。

何をするにしても、実行する必要がありますfast

更新
人々の反応を考えると、インポートユーティリティを実行する前にフィールドをクリーニングする必要があると思います。

インポートユーティリティを作成しているものの質問に答えるために、それはC#アプリです。現在、BIGINTとBIGINTを比較していますが、DBデータを変更する必要はなく、非常に小さなデータセット(約2000レコード)でパフォーマンスが低下しています。

BIGINTとBIGINTを比較すると、速度が低下しますか?

アプリのコード側をできる限り最適化しました(正規表現を削除し、不要なDB呼び出しを削除しました)。 SQLを問題の原因として特定することはできませんが、それでも問題はないと感じています。

60
Dan Herbert

誤解するかもしれませんが、データベース内の現在のデータの文字列を削除する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)
16
Scott Nichols

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
103
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

関数を作成したくない場合、またはT-SQLで1つのインライン呼び出しだけが必要な場合は、次を試すことができます。

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

もちろん、これは文字列関数からすべての特殊文字を削除する一般的なものではなく、電話番号のフォーマットを削除するためのものです。

16
Tom

シンプルな機能:

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
10
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

明らかな理由から、varcharsの操作は、数値の操作と比較して基本的に遅く、非効率的です。元の投稿でリンクする関数は、文字列内の各文字をループ処理して数字かどうかを判断するため、実際には非常に遅くなります。それを何千ものレコードに対して行うと、プロセスは遅くなるはずです。これは正規表現に最適な仕事ですが、SQL Serverではネイティブにサポートされていません。 CLR関数を使用してサポートを追加できますが、これを試すことなくこれがどれほど遅いかを言うのは難しいですが、各電話番号の各文字をループするよりも大幅に高速であると確信しています!

データベース内で電話番号をフォーマットして数字だけにする場合、SQLの数値型に切り替えると、他の数値型との比較が非常に高速になります。新しいデータの入力速度に応じて、比較対象が適切にフォーマットされていれば、データベース側でトリミングと数値への変換が十分に高速であることがわかりますが、可能であれば、データベースにアクセスする前にこれらのフォーマットの問題を処理する.NET言語でインポートユーティリティを記述しないでください。

いずれにせよ、オプションのフォーマットに関して大きな問題が発生します。番号がOriginで北米のみであることが保証されている場合でも、完全に市外局番の修飾された電話番号の前に1を置く人とそうでない人は、同じ電話番号の複数のエントリの可能性を引き起こします。さらに、データの内容によっては、複数の人が住んでいる自宅の電話番号を使用する人もいるため、一意の制約により、世帯ごとにデータベースメンバーが1人しか許可されません。作業番号を使用して同じ問題を抱える人もいれば、人為的な一意性の可能性を再び引き起こす拡張子を含めるか含めない人もいます。

これらはすべて、特定のデータと使用状況に応じて、影響を与える場合と与えない場合がありますが、留意することが重要です!

1
Grank

最初にScottのCLR関数を試しますが、更新されるレコードの数を減らすためにWHERE句を追加します。

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

レコードの大部分に数字以外の文字が含まれていることがわかっている場合は、役に立たない可能性があります。

1
Mike L

夜間のプロセスでそれらを削除して別のフィールドに保存し、プロセスを実行する直前に変更されたレコードを更新できますか?

または、挿入/更新で、後で参照するために「数値」形式を保存します。トリガーはそれを行う簡単な方法です。

1
Dan Williams

ゲームに遅れていることはわかっていますが、ここでは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
1
Dennis Allen

パフォーマンスの観点からインライン関数を使用します。以下を参照してください。「+」、「-」などの記号は削除されないことに注意してください

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文字以上で定義できます...

0
hkravitz

通常、数千のレコードに対する数千のレコードは問題になりません。 SSISを使用して、このような重複排除を使用して何百万ものレコードをインポートしました。

データベースをクリーンアップして、最初に数字以外の文字を削除し、それらを除外します。

0
Cade Roux

超シンプルなソリューションを探しています:

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

「SQLを問題の原因として特定することはできなくなりましたが、それでもそのように感じます。」

SQLプロファイラーを起動して見てみましょう。結果のクエリを取得し、実行プランをチェックして、インデックスが使用されていることを確認します。

0
Amy B