66

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

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

この投稿のスクリプトを使用してみました:
T-SQL トリム   (およびその他の英数字以外の文字)

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

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

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

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

インポート ユーティリティを何に書いているのかという質問に答えると、それは C# アプリです。現在、BIGINT と BIGINT を比較しています。DB データを変更する必要はありません。また、非常に小さなデータ セット (約 2000 レコード) でパフォーマンス ヒットが発生しています。

BIGINT と BIGINT を比較すると速度が低下する可能性はありますか?

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

4

15 に答える 15

107

このソリューションは、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
于 2011-06-30T02:44:50.297 に答える
20

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

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

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

于 2011-08-11T21:31:15.990 に答える
16

誤解しているかもしれませんが、データベース内の現在のデータの文字列を 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)
于 2008-09-19T22:46:41.060 に答える
10

シンプルな機能:

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
于 2014-03-20T11:49:17.300 に答える
6
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%^@#$^')  
于 2010-11-03T12:54:36.557 に答える
1

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

データベースでフォーマットされた電話番号を取得して、数字のみになるようにしたら、SQL で数値型に切り替えることができます。これにより、他の数値型との超高速比較が可能になります。新しいデータが入ってくる速さにもよりますが、データベース側でのトリミングと数値への変換は、比較対象が適切にフォーマットされていれば十分に高速ですが、可能であれば、より良いでしょうデータベースにアクセスする前にこれらのフォーマットの問題を処理するインポート ユーティリティを .NET 言語で作成する必要はありません。

いずれにせよ、オプションの書式設定に関して大きな問題が発生するでしょう。あなたの番号が北米のみの発信元であることが保証されている場合でも、完全に市外局番対応の電話番号の前に 1 を付ける人もいれば、そうしない人もいます。これにより、同じ電話番号が複数エントリされる可能性があります。さらに、データが何を表しているかによって、何人かの人々が住んでいる自宅の電話番号を使用する人もいるでしょう。そのため、一意の制約により、世帯ごとに 1 つのデータベース メンバーしか許可されません。仕事番号を使用して同じ問題を抱えている人もいれば、人為的な一意性の可能性を再び引き起こす拡張子を含めたり含めなかったりする人もいます。

特定のデータと使用状況に応じて、これらすべてが影響する場合と影響しない場合がありますが、覚えておくことが重要です。

于 2008-09-19T23:16:23.857 に答える
1

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

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

于 2008-09-19T22:44:54.323 に答える
1

ゲームに遅れていることはわかっていますが、ここに私が 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
于 2010-04-07T21:30:43.997 に答える
1

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

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

ただし、レコードの大部分に数字以外の文字が含まれていることがわかっている場合は、役に立たないかもしれません。

于 2008-09-19T23:47:56.467 に答える
0

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

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

于 2008-09-20T03:13:36.067 に答える
0

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

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

于 2008-09-19T23:54:44.577 に答える
0

超簡単な解決策を探しています:

SUBSTRING([Phone], CHARINDEX('(', [Phone], 1)+1, 3)
       + SUBSTRING([Phone], CHARINDEX(')', [Phone], 1)+1, 3)
       + SUBSTRING([Phone], CHARINDEX('-', [Phone], 1)+1, 4) AS Phone
于 2011-06-07T15:08:27.447 に答える
-1

データベース内の電話番号に厳密な形式を適用することをお勧めします。次の形式を使用します。(米国の電話番号を想定)

データベース: 5555555555x555

ディスプレイ: (555) 555-5555 内線 555

入力: 任意の文字列に埋め込まれた 10 桁以上の数字。(正規表現の置換により、数字以外の文字がすべて削除されます)

于 2008-09-19T22:55:00.710 に答える