SQL Azure 包含未返回所有结果
SQL Azure CONTAINS not returning all results
我们添加了对以下 table 的自由文本搜索:
| 1 | kayer-meyar |
| 2 | KA-ME |
但是,
select *
from Names
where CONTAINS(name, '"ME*"')
仅 returns:
| 1 | kayer-meyar |
同时,
select *
from Names
where CONTAINS(name, '"KA*"')
returns 两者:
| 1 | kayer-meyar |
| 2 | KA-ME |
当我们 运行:
select *
from sys.dm_fts_parser('"KA-ME"', 1033, NULL, 0)
returns:
ka-me
ka
me
尝试 运行 查询:select *
来自 sys.dm_fts_parser('"kayer-meyar"', 1033, NULL, 0)
ME 不是 Kayer-meyar 中的一个词,您可能得不到结果。
在搜索和调整你的问题后,我发现全文搜索有两个主要错误:
- 连字符可能会被视为分词。当我使用
'"ME*"'
时,它 return 只有 | 1 | kayer-meyar |
。它没有 return | 2 | KA-ME |
。问题是因为您的条件只允许单词以 (not end with
or in a middle
) ME + at least one character
开头。 你可以说,"then how come it return | 1 | kayer-meyar |
as string me
is in the middle of this word ?"。好吧,这是因为全文搜索不将其视为一个单字,而是将其视为两个单独的词(类似于 kayer meyar
),因此它满足了要求(me*
)。再次在 KA-ME
的情况下,它识别为 KA ME
而不是一个单词,它也没有满足条件(尽管它以 ME
为星标,但之后没有额外的字符)
- 您尝试过重建全文索引吗? .
现在 解决方案 是:
我已关闭全文搜索查询的停止列表
为此使用此查询(我的 table 名称是 MyTable
):
ALTER FULLTEXT INDEX ON MyTable SET STOPLIST = OFF
然后 运行 您的查询。这次你会得到你想要的结果。
这是我的完整查询:
--CREATE TABLE MyTable
--(
--Id INT IDENTITY(1,1),
--Name varchar(max) Not Null
--)
---- To see if FULLTEXT installed or not
--SELECT SERVERPROPERTY('IsFullTextInstalled')
----
---- https://technet.microsoft.com/en-us/library/ms187317.aspx
----
----
--CREATE UNIQUE INDEX ui_MyTable ON MyTable(Id);
--select name from sysindexes where object_id('MyTable') = id;
--CREATE FULLTEXT CATALOG ft AS DEFAULT;
--CREATE FULLTEXT INDEX ON MyTable(Name)
-- KEY INDEX ui_MyTable
-- WITH STOPLIST = SYSTEM;
--GO
--INSERT INTO MyTable(Name) VALUES('kayer-meyar'),('KA-ME');
ALTER FULLTEXT INDEX ON MyTable SET STOPLIST = OFF
select *
from MyTable
where CONTAINS(Name, '"ME*"')
select *
from MyTable
where CONTAINS(Name, '"KA*"')
您描述的行为是正确使用系统停用词列表的结果。这是预期的行为。 "Me"是停用词,存在于系统停用词列表中。数据索引过程默认使用的系统停用词列表。
你可以用这个脚本检查自己:
select * from sys.dm_fts_parser('"KA-ME"', 1033, 0, 0)
这里的第三个参数是停用词列表标识符。当您传递 NULL 时,停用词不会在解析时被识别,您会看到 "ME" 类型 "Exact Match"。当您将 0 作为第三个参数传递时,将使用系统停用词列表,并且 "ME" 将属于 "Noise Word" 类型。这意味着SQL服务器不会将其保存到FTS索引中进行搜索。
正如 Raihan 所说,你可以关闭系统停用词列表,但对我来说,完全关闭停用词是一个太大的锤子,尤其是对于 Azure SQL 数据库,因为你应该支付额外的费用 space(FTS索引存储在同一个数据库的内部表中)。创建一个新的(更小的)停用词列表并将其用于 FTS 可能是更好的解决方案。
我们添加了对以下 table 的自由文本搜索:
| 1 | kayer-meyar |
| 2 | KA-ME |
但是,
select *
from Names
where CONTAINS(name, '"ME*"')
仅 returns:
| 1 | kayer-meyar |
同时,
select *
from Names
where CONTAINS(name, '"KA*"')
returns 两者:
| 1 | kayer-meyar |
| 2 | KA-ME |
当我们 运行:
select *
from sys.dm_fts_parser('"KA-ME"', 1033, NULL, 0)
returns:
ka-me
ka
me
尝试 运行 查询:select * 来自 sys.dm_fts_parser('"kayer-meyar"', 1033, NULL, 0)
ME 不是 Kayer-meyar 中的一个词,您可能得不到结果。
在搜索和调整你的问题后,我发现全文搜索有两个主要错误:
- 连字符可能会被视为分词。当我使用
'"ME*"'
时,它 return 只有| 1 | kayer-meyar |
。它没有 return| 2 | KA-ME |
。问题是因为您的条件只允许单词以 (notend with
orin a middle
)ME + at least one character
开头。 你可以说,"then how come it return| 1 | kayer-meyar |
as stringme
is in the middle of this word ?"。好吧,这是因为全文搜索不将其视为一个单字,而是将其视为两个单独的词(类似于kayer meyar
),因此它满足了要求(me*
)。再次在KA-ME
的情况下,它识别为KA ME
而不是一个单词,它也没有满足条件(尽管它以ME
为星标,但之后没有额外的字符) - 您尝试过重建全文索引吗? .
现在 解决方案 是:
我已关闭全文搜索查询的停止列表
为此使用此查询(我的 table 名称是 MyTable
):
ALTER FULLTEXT INDEX ON MyTable SET STOPLIST = OFF
然后 运行 您的查询。这次你会得到你想要的结果。
这是我的完整查询:
--CREATE TABLE MyTable
--(
--Id INT IDENTITY(1,1),
--Name varchar(max) Not Null
--)
---- To see if FULLTEXT installed or not
--SELECT SERVERPROPERTY('IsFullTextInstalled')
----
---- https://technet.microsoft.com/en-us/library/ms187317.aspx
----
----
--CREATE UNIQUE INDEX ui_MyTable ON MyTable(Id);
--select name from sysindexes where object_id('MyTable') = id;
--CREATE FULLTEXT CATALOG ft AS DEFAULT;
--CREATE FULLTEXT INDEX ON MyTable(Name)
-- KEY INDEX ui_MyTable
-- WITH STOPLIST = SYSTEM;
--GO
--INSERT INTO MyTable(Name) VALUES('kayer-meyar'),('KA-ME');
ALTER FULLTEXT INDEX ON MyTable SET STOPLIST = OFF
select *
from MyTable
where CONTAINS(Name, '"ME*"')
select *
from MyTable
where CONTAINS(Name, '"KA*"')
您描述的行为是正确使用系统停用词列表的结果。这是预期的行为。 "Me"是停用词,存在于系统停用词列表中。数据索引过程默认使用的系统停用词列表。
你可以用这个脚本检查自己:
select * from sys.dm_fts_parser('"KA-ME"', 1033, 0, 0)
这里的第三个参数是停用词列表标识符。当您传递 NULL 时,停用词不会在解析时被识别,您会看到 "ME" 类型 "Exact Match"。当您将 0 作为第三个参数传递时,将使用系统停用词列表,并且 "ME" 将属于 "Noise Word" 类型。这意味着SQL服务器不会将其保存到FTS索引中进行搜索。
正如 Raihan 所说,你可以关闭系统停用词列表,但对我来说,完全关闭停用词是一个太大的锤子,尤其是对于 Azure SQL 数据库,因为你应该支付额外的费用 space(FTS索引存储在同一个数据库的内部表中)。创建一个新的(更小的)停用词列表并将其用于 FTS 可能是更好的解决方案。