web-dev-qa-db-ja.com

カーソルとあいまい文字列照合を使用して、テーブル内の冗長な可能性のあるすべてのレコードを検索します

私は興味深い挑戦を与えられ、私には有効なアプローチがあると信じていますが、それを実装する前に、物事を行うためのより良い方法があるかどうかを確認したいと思います。

問題
顧客の予約を追跡するコールセンターがあり、顧客がシステムに存在しない場合は、新しい顧客レコードを作成します。コールセンターが適切な検索を実行せず、したがって顧客を見つけられず、その結果、名前と連絡先情報が異なる顧客の冗長なレコードが作成されるという一般的なケースがあります。これらの冗長なレコードのグループ化を反映するレポートを作成するのが私の仕事です。

例:jCam Example

各行のバリエーション(IDを考慮しない)のために、特定の列で完全に一致するものを探すことができないことがわかります。それでも、進行中のデータ入力エラーを監視できるように、これらのそれぞれをレポートにグループ化する必要があります。

私の提案する解決策
カーソルを使用したことがないので、カーソルの学習に取り掛かる必要がありますが、テーブル内の他のすべてのレコードに対して各レコードを評価できるように、2つのカーソルをネストする必要があると思います。 (ネストされたカーソルの結果として)2つのラインアイテムを比較するたびに、[First Name]、[Last Name]、および[Email]フィールドを Levenshtein Distance Function と比較します。そこで完全に一致するものを見つけるために6つの電話番号の比較を実行し、結果の行をテーブルに書き込んで、レポートを取得するための参照ポイントとして使用できるようにします。

例:fuzzyResults

例の最初の行が結果の4行と比較されることがわかります。次に、ユーザーが設定したパラメーターに該当する結果を検索できるフロントエンドをユーザーに提供し、特定の列または列の組み合わせの精度を指定できます。これにはかなりのオーバーヘッドが発生する可能性があるため、これを一晩実行する可能性が高く、エンドユーザー向けのレポートはすべてこの派生インデックステーブルに基づいています。

ですから、繰り返しになりますが、私は自分のアプローチに対する批評を探しています。これは珍しい問題ではないと感じており、包括的で最適化されたソリューションがコミュニティ全体に役立つと感じています。

-編集-

だから私はそれがしたいことをするsprocを書きました:

ALTER PROCEDURE [dbo].[sp_Cust_BuildIndex]
AS
BEGIN

SET NOCOUNT ON;

DECLARE @ID1 VARCHAR(50)
DECLARE @ID2 VARCHAR(50)
DECLARE @distFN Int
DECLARE @distLN Int
DECLARE @distEmail Int
DECLARE @HH Int
DECLARE @HW Int
DECLARE @HM Int
DECLARE @WH Int
DECLARE @WW Int
DECLARE @WM Int
DECLARE @MH Int
DECLARE @MW Int
DECLARE @MM Int

DECLARE @FN1 VARCHAR(256)
DECLARE @FN2 VARCHAR(256)
DECLARE @LN1 VARCHAR(256)
DECLARE @LN2 VARCHAR(256)
DECLARE @Email1 VARCHAR(256)
DECLARE @Email2 VARCHAR(256)
DECLARE @Phone1 VARCHAR(20)
DECLARE @Phone2 VARCHAR(20)
DECLARE @Mobile1 VARCHAR(20)
DECLARE @Mobile2 VARCHAR(20)
DECLARE @Work1 VARCHAR(20)
DECLARE @Work2 VARCHAR(20)

DELETE FROM dbThis.dbo.[tbl_Cust_Dup_Index]

DECLARE db_ID1 CURSOR FOR  
SELECT [ID]
FROM [dbThat].[dbo].[CUSTOMER]

OPEN db_ID1   
FETCH NEXT FROM db_ID1 INTO @ID1

    SELECT @FN1 = [FIRSTNAME]
          ,@LN1 = [LASTNAME]
          ,@Phone1 = [TEL_HOME]
          ,@Work1 = [TEL_WORK]
          ,@Mobile1 = [TEL_MOBIL]
          ,@Email1 = [EMAIL]
    FROM [SPABIZ_MIRROR].[dbo].[CUSTOMER]
    WHERE ID = @ID1 and [Delete] = 0

    DECLARE db_ID2 CURSOR FOR  
    SELECT [ID]
    FROM [dbThat].[dbo].[CUSTOMER]

    OPEN db_ID2   
    FETCH NEXT FROM db_ID1 INTO @ID2   

    WHILE @@FETCH_STATUS = 0   
    BEGIN
        IF @ID1 <> @ID2
        BEGIN

           SELECT  @FN2 = [FIRSTNAME]
                  ,@LN2 = [LASTNAME]
                  ,@Phone2 = [TEL_HOME]
                  ,@Work2 = [TEL_WORK]
                  ,@Mobile2 = [TEL_MOBIL]
                  ,@Email2 = [EMAIL]
          FROM [dbThat].[dbo].[CUSTOMER]
          WHERE ID = @ID2 and [Delete] = 0

          SET @HH = dbThis.dbo.fn_Phone_Bool(@Phone1, @Phone2)
          SET @HW = dbThis.dbo.fn_Phone_Bool(@Phone1, @Work2)
          SET @HM = dbThis.dbo.fn_Phone_Bool(@Phone1, @Mobile2)
          SET @WH = dbThis.dbo.fn_Phone_Bool(@Work1, @Phone2)
          SET @WW = dbThis.dbo.fn_Phone_Bool(@Work1, @Work2)
          SET @WM = dbThis.dbo.fn_Phone_Bool(@Work1, @Mobile2)
          SET @MH = dbThis.dbo.fn_Phone_Bool(@Mobile1, @Phone2)
          SET @MW = dbThis.dbo.fn_Phone_Bool(@Mobile1, @Work2)
          SET @MM = dbThis.dbo.fn_Phone_Bool(@Mobile1, @Mobile2)

          Insert Into dbThis.dbo.[tbl_Cust_Dup_Index]
          VALUES (@ID1
                ,@ID2
                ,dbThis.dbo.edit_distance(@FN1, @FN2)
                ,dbThis.dbo.edit_distance(@LN1, @LN2)
                ,dbThis.dbo.edit_distance(@Email1, @Email2)
                ,@HH
                ,@HW
                ,@HM
                ,@WH
                ,@WW
                ,@WM
                ,@MH
                ,@MW
                ,@MM) 
        END
           FETCH NEXT FROM db_ID2 INTO @ID2   
    END   

    CLOSE db_ID2   
    DEALLOCATE db_ID2

CLOSE db_ID1   
DEALLOCATE db_ID1

上記のレーベンシュタイン距離リンクの関数を使用し、一致する場合は0を返し、一致しない場合は1を返す「fn_Phone_Bool」関数を作成しました。これにより、私が求めていた種類のインデックステーブルが作成されます。

indexResults

悲しいことに、それは完全に驚くことではないforEEEEEEEVERを取ります。 1時間の検索で、22000件の一致が生成されました。したがって、そのレートで、約377,000の顧客レコードのテーブル全体を調べ、それらを相互に比較して、768年強で約141.8bの一致を作成すると考えています。そのような時間はないので、ネストされたカーソルSelectを巧みに使用する必要があります。また、すべてが完全に一致するわけではないため、電子メールのedit_distancesに明らかに問題があります。

私の考えでは、最初のSelect(クラスター?)はすべてのIDを取得し、2番目のSelectは「where」句で「like」を使用して2番目のクラスターをはるかに小さな結果セットに分離します。結果をレコードごとに数回の一致に制限できる場合は、このレポートを毎週実行することも、実行しないこともできます。

いつものように、提案は大歓迎です!

2
n8.

少し「バージョン1」であることは知っていますが、Data Quality Services(DQS)はこの種のことを実行できるはずです。私はあなたのデータを取り、簡単なウォークスルーを行い(おおよそ ここ に従う)、いくつかの簡単なドメインを構築し、ナレッジベースをトレーニングして「キャンベル」を「キャンベル」に変換し、いくつかの良い結果を得ました。 DQS project example

次に、結果をテーブル(またはExcelファイルまたは.csv)にエクスポートし、そこで元の値を更新/重複排除するために使用できます。出力には、ドメインに対するあいまい一致に基づく「信頼度」スコアが含まれます。あなたがこれのあなた自身のバージョンを転がす前に一見の価値があるかもしれませんか?ナレッジベースは、時間の経過とともにさらに役立つ可能性があります。たとえば、キャンベルからキャンベルに変換するようにトレーニングした後は、再度行う必要はありません。

DQSデータマッチングプロジェクトも適切です-例: ここ

2
wBob