sql >> データベース >  >> RDS >> Sqlserver

一致率を使用したSQLServerあいまい検索

    私ができる最善のことは、クエリの一部を単純化し、それをテーブル値関数に変更することです。スカラー関数はパフォーマンスが低いことで有名です。インラインTVFの利点は、ビューのようにクエリ定義がメインクエリに拡張されることです。

    これにより、私が行ったテストの実行時間が大幅に短縮されます。

    ALTER FUNCTION dbo.FuzySearchTVF (@Reference VARCHAR(200), @Target VARCHAR(200))
    RETURNS TABLE
    AS
    RETURN
    (   WITH N (n) AS 
        (   SELECT  TOP (ISNULL(CASE WHEN DATALENGTH(@Reference) > DATALENGTH(@Target) 
                                        THEN DATALENGTH(@Reference) 
                                    ELSE DATALENGTH(@Target) 
                                END, 0))    
                    ROW_NUMBER() OVER(ORDER BY n1.n)
            FROM    (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) AS N1 (n)
            CROSS JOIN (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) AS N2 (n)
            CROSS JOIN (VALUES (1), (1)) AS N3 (n)
            WHERE   @Reference IS NOT NULL AND @Target IS NOT NULL
        ), Src AS
        (   SELECT  Reference = CASE WHEN DATALENGTH(@Reference) > DATALENGTH(@Target) THEN @Reference
                                    ELSE @Reference + REPLICATE('_', DATALENGTH(@Target) - DATALENGTH(@Reference))
                                END,
                    Target = CASE WHEN DATALENGTH(@Target) > DATALENGTH(@Reference) THEN @Target
                                    ELSE @Target + REPLICATE('_', DATALENGTH(@Target) - DATALENGTH(@Reference))
                                END,
                    WordLength = CASE WHEN DATALENGTH(@Reference) > DATALENGTH(@Target) THEN DATALENGTH(@Reference) ELSE DATALENGTH(@Target) END
            WHERE   @Reference IS NOT NULL 
            AND     @Target IS NOT NULL
            AND     @Reference != @Target
        ), Scores AS
        (   SELECT  seq = t1.n ,
                    Letter = SUBSTRING(s.Reference, t1.n, 1),
                    s.WordLength ,
                    LetterScore = s.WordLength - ISNULL(MIN(ABS(t1.n - t2.n)), s.WordLength)
            FROM    Src AS s
                    CROSS JOIN N AS t1
                    INNER JOIN N AS t2
                        ON SUBSTRING(@Target, t2.n, 1) = SUBSTRING(s.Reference, t1.n, 1)
            WHERE   @Reference IS NOT NULL 
            AND     @Target IS NOT NULL
            AND     @Reference != @Target
            GROUP BY t1.n, SUBSTRING(s.Reference, t1.n, 1), s.WordLength
        )
        SELECT  [Score] = 100 
        WHERE   @Reference = @Target
        UNION ALL
        SELECT  0
        WHERE   @Reference IS NULL OR @Target IS NULL
        UNION ALL
        SELECT  CAST(SUM(LetterScore) * 100.0 / MAX(WordLength * WordLength) AS NUMERIC(5, 2))
        FROM    Scores
        WHERE   @Reference IS NOT NULL 
        AND     @Target IS NOT NULL
        AND     @Reference != @Target
        GROUP BY WordLength
    );
    

    そして、これは次のように呼ばれます:

    SELECT  f.Score
    FROM    dbo.Customer AS c
            CROSS APPLY [dbo].[FuzySearch]('First Name Middle Name Last Name', c.FirstName) AS f
    

    ただし、それでもかなり複雑な関数であり、顧客テーブルのレコード数によっては、1秒に短縮するのは少し難しいと思います。



    1. 事前入力されたデータベースはAPI28で機能せず、そのようなテーブル例外はスローされません

    2. t-sql selectは、年の範囲内のすべての月を取得します

    3. 左側のvarcharを特定の長さにパディングする最も効率的なT-SQLの方法は?

    4. PostgreSQL 9.5以降のJSON配列への追加(プッシュ)と削除