7

我有一个用户表,其中包含一个人名的单列:

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

如果您使用全文索引,您将获得各种功能,例如动词词干和词库,请参阅全文搜索中的语言组件和语言支持,在名称列表中搜索时您不需要这些功能。顺便说一句,这些功能是依赖于语言的,这就是您在全文索引上指定语言的原因。

您甚至可能希望避免使用停止列表。至少我会,因为在荷兰语中,许多姓氏都以冠词和/或介词开头:“Rembrandt van Rijn”。“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 回答