我有一个用户表,其中包含一个人姓名列:
CREATE TABLE [dbo].[Users]
(
Id bigint NOT NULL,
Name nvarchar(80) NOT NULL,
PRIMARY KEY CLUSTERED (Id ASC)
)
Name
列可以包含全名或只包含名字或任何内容(以空格分隔)。为了在Name
上实现搜索,我想利用SQL的全文搜索,但不确定它是否适合搜索名称/昵称而不是实际单词。另外一个问题是 - 在Name
上创建FT索引时,我选择哪种语言?
还有其他考虑吗?
谢谢。
答案 0 :(得分:2)
乍一看,我建议使用LIKE运算符而不是全文查询。
确保搜索不区分大小写且可能不区分不敏感的内容。这可以通过在服务器,数据库,表列或查询中设置正确的排序规则来实现。在查询中,这可以通过以下方式完成:
SELECT *
FROM [dbo].[Users]
WHERE Name LIKE '%niaher%' COLLATE SQL_Latin1_General_CP1_CI_AI
如果您使用全文索引,则在搜索名称列表时,您会获得各种功能,例如动词词干和词库,请参阅Linguistic Components and Language Support in Full-Text Search,这是您不需要的。顺便说一句,这些功能是依赖于语言的,这就是你在全文索引上指定语言的原因。
您可能希望避免使用停止列表。至少我会,因为在荷兰语中,许多姓氏都以文章和/或介词开头:“伦勃朗 van Rijn”。 “van”肯定会出现在荷兰的停止列表中,并阻止对包含“van”的搜索字词进行任何匹配。
如果遇到性能问题,尝试全文索引并使用CONTAINS with a simple term进行搜索可能会很有用。
SELECT *
FROM [dbo].[Users]
WHERE CONTAINS(Name, 'niaher')
请注意,全文索引是异步更新的。
答案 1 :(得分:1)
似乎如果你想搜索多部分名称,全文搜索是最简单和最合适的方法(如果我错了,请纠正我)。另一种选择是LIKE '%query%'
,但它有太多缺点:
所以我继续实施全文搜索。我的查询看起来像这样:
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 - 我写了一篇blogpost来记录这个。