Mag*_*ith 5 sql-server full-text-search
我在 Microsoft SQL Server 2008 R2 (SP3) 中有一个存储过程,它在表中搜索与@Keywords我传入的参数匹配的任何内容。LIKE由于该表有 155,000 行,所以使用速度太慢,所以我创建了一个全文目录并切换到使用CONTAINS反而。我对这个功能不是很熟悉,但在研究之后似乎我需要做的就是处理用户选择的单词:
SET @Keywords = '"' + @Keywords + '*"'
Run Code Online (Sandbox Code Playgroud)
添加双引号和星号效果很好,直到有人发现在搜索他们知道肯定存在的字符串时没有结果。似乎*正在停止CONTAINS查找@Keywords位于字符串最末尾的行。
使用非常简单的 t-SQL 进行分析:
--first I prove that I can find the one row which the user was searching for
--this column is a NVARCHAR(200)
SELECT * FROM News
WHERE Headline = '1120 days at sea and still sailing' --1 row returned (OK)
SELECT * FROM News
WHERE Headline LIKE '1120 days at sea and still sailing%' --1 row returned (OK)
--using the full headline
SELECT * FROM News
WHERE CONTAINS(Headline, '"1120 days at sea and still sailing*"') --no rows, just like my proc (WRONG)
SELECT * FROM News
WHERE CONTAINS(Headline, '"1120 days at sea and still sailing"') --1 row returned if asterisk omitted (OK)
--using part of the headline so we don't touch the end of the
SELECT * FROM News
WHERE CONTAINS(Headline, '"days at sea and still"') --lots of rows, since it is anything with 'days' and then 'sea' present (NOT USEFUL)
SELECT * FROM News
WHERE CONTAINS(Headline, '"days at sea and still*"') --no rows (WRONG)
Run Code Online (Sandbox Code Playgroud)
我注意到,CONTAINS考虑在/和/仍然是“噪音”字并且忽略它们。很公平。航行这个词是字符串末尾的麻烦词。
我的问题:
我应该如何修改传入的关键字以确保CONTAINS无论列值中关键字字符串的位置如何,都可以找到结果?
这与添加星号 (*) 与 组合时如何处理非索引字表(或干扰词)有关CONTAINS()。
对此有更长的解释。
简而言之:默认 <> 前缀搜索
默认情况下,使用非SYSTEM索引字表时,某些单词不会被索引并分类为非索引字表单词。
添加 * + 时,CONTAINS()它们不会被视为非索引字词,并且确切的短语必须匹配。但是,当使用默认非SYSTEM索引字表时,这些“非索引字表单词”不会添加到索引中,导致找不到匹配项。
问题
您是说,当存在星号时,预期的行为是为包含干扰词的用户返回零匹配项?当您考虑大多数人可能会输入的内容时,这似乎令人惊讶!
前缀匹配与 Microsoft 文档中的 contains 相结合
指定以指定文本开头的单词或短语的匹配。将前缀术语括在双引号 ("") 中,并在结束引号前添加星号 (*),以便匹配以星号之前指定的简单术语开头的所有文本
这里的关键词是:
以便匹配以星号之前指定的简单术语开头的所有文本
这意味着我们在使用前缀匹配 和 时必须具有精确匹配CONTAINS()。
解决方法#1 禁用非索引字表
解决此问题的一种方法是禁用“非索引字表”
ALTER FULLTEXT INDEX ON News
SET STOPLIST OFF;
Run Code Online (Sandbox Code Playgroud)
您必须知道,这会增加存储在全文索引中的单词,因为单词“and”、“or”、“still”等被添加到索引中。
禁用非索引字列表可能会影响性能。
重新启用系统非索引字表
ALTER FULLTEXT INDEX ON News
SET STOPLIST SYSTEM;
Run Code Online (Sandbox Code Playgroud)
或者,您可以创建自己的非索引字表
根据您的查询对此进行测试:
ALTER FULLTEXT INDEX ON News
SET STOPLIST OFF;
INSERT INTO News(Headline)
VALUES('1120 days at sea and still sailing')
INSERT INTO News(Headline)
VALUES('zzz 1120 days at sea and still sailing')
SELECT * FROM News
WHERE CONTAINS(Headline, '"days at sea and still*"')
Run Code Online (Sandbox Code Playgroud)
结果
id Headline
1 1120 days at sea and still sailing
2 zzz 1120 days at sea and still sailing
Run Code Online (Sandbox Code Playgroud)
对于其他解决方法,您可以考虑使用CONTAINS()或FREETEXT()不使用前缀匹配,或使用前缀匹配 +CONTAINS()与一个(非噪音)单词以获得更准确的结果
在您的情况下,启用transform noise words似乎不适用于前缀匹配
sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
sp_configure 'transform noise words', 1;
RECONFIGURE;
GO
Run Code Online (Sandbox Code Playgroud)
再次由于前缀匹配 + contains 的工作方式。
SELECT * FROM News
WHERE CONTAINS(Headline, '"1120 days at sea and still sailing*"')
Run Code Online (Sandbox Code Playgroud)
- 没有结果
在全文索引内(使用 stoplist = system)。
SELECT display_term,column_id,document_count
FROM sys.dm_fts_index_keywords(DB_ID('MNGDB'), OBJECT_ID('dbo.News'));
Run Code Online (Sandbox Code Playgroud)
结果
display_term column_id document_count
1120 2 2
days 2 2
nn1120 2 2
sailing 2 2
sea 2 2
zzz 2 1
END OF FILE 2 2
Run Code Online (Sandbox Code Playgroud)
'Still'、'at'、'and' 被过滤掉,禁用非索引字表时它们将被添加到索引中
无停用词测试
ALTER FULLTEXT INDEX ON News
SET STOPLIST SYSTEM;
INSERT INTO dbo.News
VALUES('days sea sailing')
SELECT * FROM dbo.News
WHERE CONTAINS(Headline, '"days sea sailing*"')
id Headline
4 days sea sailing
Run Code Online (Sandbox Code Playgroud)
更多关于星号的信息
星号的存在启用前缀匹配模式...如果提供了短语,则如果该列包含所有指定单词且最终单词后有零个或多个其他字符,则会检测到匹配项