7

個人の名前の列が 1 つのユーザー テーブルがあります。

CREATE TABLE [dbo].[Users]
(
    Id bigint NOT NULL,
    Name nvarchar(80) NOT NULL,
    PRIMARY KEY CLUSTERED (Id ASC)
)

列にはName、フル ネームまたは名前のみ、または実際の名前 (スペースで区切られたもの) を含めることができます。で検索を実装するためにName、SQL の全文検索を利用したいのですが、実際の単語ではなく名前/ニックネームの検索に適しているかどうかわかりません。また、問題は、FT インデックスを作成するときにどの言語を選択すればよいかということNameです。

その他の考慮事項はありますか?

ありがとうございました。

4

2 に答える 2

4

一見すると、フルテキストクエリではなくLIKE演算子を使用することをお勧めします。

大文字と小文字を区別せず、おそらくアクセントを区別しない検索を行ってください。これは、サーバー、データベース、テーブル列、またはクエリのいずれかに正しい照合を設定することで実現できます。クエリでは、これは次のような方法で実行されます。

SELECT *
FROM [dbo].[Users]
WHERE Name LIKE '%niaher%' COLLATE SQL_Latin1_General_CP1_CI_AI

全文索引を使用すると、動詞のステミングやシソーラスなど、あらゆる種類の機能を利用できます。名前のリストを検索する場合は、必要のない全文検索の言語コンポーネントと言語サポートを参照してください。ちなみに、これらの機能は言語に依存しているため、フルテキストインデックスで言語を指定します。

避けたいストップリストの適用。少なくとも私は、オランダ語では多くの家系の名前が冠詞や前置詞で始まるので、「レンブラント・ファン・レイン」と言います。「van」は確実にオランダのストップリストに含まれ、「van」を含む検索語での一致を防ぎます。

パフォーマンスの問題が発生した場合は、フルテキストインデックスを試して、単純な用語でCONTAINSを使用して検索すると便利な場合があります。

SELECT *
FROM [dbo].[Users]
WHERE CONTAINS(Name, 'niaher')

フルテキストインデックスは非同期で更新されることに注意してください。

于 2012-11-21T20:37:09.640 に答える
2

複数の部分からなる名前を検索する場合は、全文検索が最も簡単で適切な方法のようです (間違っていたら訂正してください)。他の選択肢は ですがLIKE '%query%'、欠点が多すぎます。

  • インデックススキャンを行うため、ひどいパフォーマンス
  • 用語の順序が重要です。たとえば、「John Smith」と「Smith John」を検索すると、異なる結果が返されます。
  • 単語の境界は無視されます。たとえば、"Ann" を検索すると "Joanna" と "Danny" も検索されますが、これらは有効な一致ではありません。

そこで、全文検索を実装しました。私のクエリは次のようになります。

SELECT * FROM Users WHERE CONTAINS(Name, '"John*"')

唯一の難点は、ユーザー クエリ (John) を CONTAINS に適したクエリ ("John*") に変換しなければならなかったことです。そのために、UserRepository に次のメソッドを実装しました。

/// <summary>
/// Converts user-entered search query into a query that can be consumed by CONTAINS keyword of SQL Server.
/// </summary>
/// <example>If query is "John S Ju", the result will be "\"John*\" AND \"S*\" AND \"Ju*\"".</example>
/// <param name="query">Query entered by user.</param>
/// <returns>String instance.</returns>
public static string GetContainsQuery(string query)
{
    string containsQuery = string.Empty;

    var terms = query.Split(new[] { ' ' }, StringSplitOptions.None);

    if (terms.Length > 1)
    {
        for (int i = 0; i < terms.Length; i++)
        {
            string term = terms[i].Trim();

            // Add wildcard term, e.g. - "term*". The reason to add wildcard is because we want
            // to allow search by partially entered name parts (partially entered first name and/or
            // partially entered last name, etc).
            containsQuery += "\"" + term + "*\"";

            // If it's not the last term.
            if (i < terms.Length - 1)
            {
                // We want all terms inside user query to match.
                containsQuery += " AND ";
            }
        }

        containsQuery = containsQuery.Trim();
    }
    else
    {
        containsQuery = "\"" + query + "*\"";
    }

    return containsQuery;
}

これが同じ問題に遭遇した人に役立つことを願っています.

PS - これを文書化したブログ投稿を書きました。

于 2012-11-22T04:48:57.810 に答える