web-dev-qa-db-ja.com

オンデマンドファジールックアップ

ビジネスの観点からは、重複している多くのレコードが含まれているCustomersテーブルがあります(誰がそうではありませんか?)。 SSISパッケージを作成してファジーグループ化を実行し、重複の可能性について報告することができました。

さて、誰かが新しい顧客を入力するのと同じように、この種の分析をしたいとします。アイデアは、顧客名(およびおそらく郵便番号のような他のいくつかの基本情報)に対してあいまい検索を実行し、顧客作成フォームに進む前に潜在的な重複を表示することです。

ここで明らかな問題は、ファジーグループ化とルックアップコンポーネントがSSISの一部であることです。これらをオンデマンドで実行したい場合は、検索用語をステージングテーブルに入れ、SSISパッケージを実行し、それが完了するのを待って、出力テーブルから結果をフェッチするなど、非常識なことをしなければなりません。それは遅く、苦痛であり、深刻な並行性の問題を抱えています。

したがって、もう1つのアイデアは、フルテキストインデックスを使用することでした。試してみると、ふさわしくないようです。顧客名の微妙なスペルミス、または「会社」対「会社」対「会社」、または「アンダーソン」対「アンデルセン」、および他のそのようなバリエーションで異なる名前は検出できません。

T-SQLからのファジーグループ化/マッチングの柔軟性を可能にするものはありますか?ファジールックアップでトークンを保存するように指示することはできますが、トークンを使用するには、ほとんどのマッチングアルゴリズムを再実装する必要があるようです。

6
db2

過去に、.Net CLR関数で「ファジー検索」を作成しました。この関数は、ユーザー定義関数が呼び出されるのと同じ方法で呼び出されます。

例えば、

select id, name
from customers
where dbo.CompareStrings("newCustomerName", customers.name) > .8

入力された名前と80%似ている名前を持つ顧客のみを返します。

一致率は、異なる文字数ではなく、1つの値を別の値に変換するために必要な変更の数に基づいています。住所を比較するために使用しましたが、使用されている多数の略語があるため、これがより効果的であることがわかりました。

文字列の比較に使用したコードは次のとおりです。ずっと前にこれを実行したので、展開方法を思い出せませんが、クイック検索で SQL CLR関数の作成方法 に関する多くの記事が表示されます

' Checks two strings against each other and returns a decimal between 0 (doesn't match at all) and 1 (100% match)
<Microsoft.SqlServer.Server.SqlFunction()> _
Public Shared Function CompareStrings(ByVal input1 As SqlChars, ByVal input2 As SqlChars) _
As <SqlFacet(Precision:=10, Scale:=4)> SqlDecimal

    If IsNothing(input1) And IsNothing(input2) Then
        Return New SqlDecimal(1.0)
    ElseIf IsNothing(input1) Or IsNothing(input2) Then
        Return New SqlDecimal(0.0)
    End If

    Dim s1 As String = New String(input1.Value)
    Dim s2 As String = New String(input2.Value)

    If s1.Length = 0 Or s2.Length = 0 Then
        Return New SqlDecimal(1.0)
    Else
        Dim re As New Regex("[^A-Za-z0-9 ]", RegexOptions.IgnorePatternWhitespace Xor RegexOptions.Singleline)
        s1 = re.Replace(s1, "( )\1*", "$1")
        s2 = re.Replace(s2, "( )\1*", "$1")
        s1 = UCase(re.Replace(s1, ""))
        s2 = UCase(re.Replace(s2.ToString, ""))

        Dim dif As Integer = GetStringSimilarity(s1, s2)
        Dim max As Integer = s1.Length
        If s2.Length > max Then max = s2.Length

        Return New SqlDecimal(1.0 - (dif / max))
    End If
End Function

' Compares two strings using the relationship in patterns of letters
Private Shared Function GetStringSimilarity(ByVal s1 As String, ByVal s2 As String) As Integer
    Dim n As Integer = s1.Length
    Dim m As Integer = s2.Length
    Dim distance(n + 1, m + 1) As Integer

    Dim cost As Integer = 0
    If n = 0 Then Return m
    If m = 0 Then Return n

    For i As Integer = 0 To n
        distance(i, 0) = i
    Next
    For j As Integer = 0 To m
        distance(0, j) = j
    Next

    For i As Integer = 1 To n
        For j As Integer = 1 To m
            If Mid(s2, j, 1) = Mid(s1, i, 1) Then cost = 0 Else cost = 1
            distance(i, j) = Min3(distance(i - 1, j) + 1, distance(i, j - 1) + 1, distance(i - 1, j - 1) + cost)
        Next
    Next
    Return distance(n, m)
End Function

' Returns the min of 3 values
Private Shared Function Min3(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer
    Dim min As Integer = x
    If y < min Then min = y
    If z < min Then min = z
    Return min
End Function
2
Rachel