为什么 SQL Server Full Text Search 索引 SCR 或 SUR 首字母缩略词后跟一个数字?

Why is SQL Server Full Text Search indexing SCR or SUR acronym followed by a number, together?

我发现 SQL 服务器全文搜索的一个非常奇怪的行为是索引 SUR、SCR 和可能的其他一些首字母缩略词,连同它后面的数字 - 作为“完全匹配”。

SELECT * FROM sys.dm_fts_parser ('"SUR 12345"', 1033, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
s u r 1 2 3 4 5 1 0 1 Exact Match sur 12345 0 SUR 12345
n n 1 2 3 4 5 s u r 1 0 1 Exact Match nn12345sur 0 SUR 12345
SELECT * FROM sys.dm_fts_parser ('"SCR 12345"', 1033, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
s c r 1 2 3 4 5 1 0 1 Exact Match scr 12345 0 SCR 12345
n n 1 2 3 4 5 s c r 1 0 1 Exact Match nn12345scr 0 SCR 12345

其他首字母缩略词或文本,包括小写 sur,不受影响:

SELECT * FROM sys.dm_fts_parser ('"sur 12345"', 1033, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
s u r 1 0 1 Exact Match sur 0 sur 12345
1 2 3 4 5 1 0 2 Exact Match 12345 0 sur 12345
n n 1 2 3 4 5 1 0 2 Exact Match nn12345 0 sur 12345
SELECT * FROM sys.dm_fts_parser ('"ABC 12345"', 1033, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
a b c 1 0 1 Exact Match abc 0 ABC 12345
1 2 3 4 5 1 0 2 Exact Match 12345 0 ABC 12345
n n 1 2 3 4 5 1 0 2 Exact Match nn12345 0 ABC 12345
SELECT * FROM sys.dm_fts_parser ('"XYZ 76"', 1033, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
x y z 1 0 1 Exact Match xyz 0 XYZ 76
7 6 1 0 2 Exact Match 76 0 XYZ 76
n n 7 6 1 0 2 Exact Match nn76 0 XYZ 76

这种行为似乎出乎意料,很可能是越野车,但我也可能遗漏了一些与分词器相关的明显内容(尝试过 1033 和 2057 - 同样的效果)。我在 SQL Server 2019 Linux 15.0.4053.23 和 2017 CU20 和 CU25 上复制了它,我可以即时访问它们。

有没有人有类似的问题和解决方案,以便 SUR、SCR 和任何其他可能损坏的首字母缩略词将独立于以下数字编入索引?

编辑:

将语言更改为 0(中性)会导致奇怪的行为 - 当使用 SUR 首字母缩略词时它不会解决问题,但会修复 SCR 首字母缩略词!

SELECT * FROM sys.dm_fts_parser ('"SUR 12345"', 0, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
s u r 1 2 3 4 5 1 0 1 Exact Match sur 12345 0 SUR 12345
n n 1 2 3 4 5 s u r 1 0 1 Exact Match nn12345sur 0 SUR 12345
SELECT * FROM sys.dm_fts_parser ('"SCR 12345"', 0, 0, 0)
keyword group_id phrase_id occurrence special_term display_term expansion_type source_term
s c r 1 0 1 Exact Match scr 0 SCR 12345
1 2 3 4 5 1 0 2 Exact Match 12345 0 SCR 12345
n n 1 2 3 4 5 1 0 2 Exact Match nn12345 0 SCR 12345

我决定悬赏这个问题,因为理想情况下我需要通过重新配置数据库索引来解决找不到搜索词的问题。

为了帮助重现下面的问题,我们提供了一个创建数据库的脚本(带有注释掉的 DROP 脚本以帮助重置状态)

/*
DROP FULLTEXT INDEX ON EnglishTexts
DROP FULLTEXT INDEX ON NeutralTexts
DROP FULLTEXT CATALOG TestSearchCatalog
USE master
DROP DATABASE TestSearch
*/

CREATE DATABASE TestSearch
GO

USE [TestSearch]
GO

CREATE FULLTEXT CATALOG TestSearchCatalog WITH ACCENT_SENSITIVITY = OFF
GO

CREATE TABLE EnglishTexts (Id INT IDENTITY(1,1) NOT NULL, Text NVARCHAR(MAX), CONSTRAINT PK_EnglishTexts PRIMARY KEY CLUSTERED (Id))
CREATE FULLTEXT INDEX ON EnglishTexts (Text LANGUAGE 'English') KEY INDEX PK_EnglishTexts ON ([TestSearchCatalog]) WITH (CHANGE_TRACKING = AUTO, STOPLIST = OFF)
INSERT INTO EnglishTexts(Text) VALUES ('PRFX 12233')
INSERT INTO EnglishTexts(Text) VALUES ('SUR 12233')
INSERT INTO EnglishTexts(Text) VALUES ('SCR 12233')

CREATE TABLE NeutralTexts (Id INT IDENTITY(1,1) NOT NULL, Text NVARCHAR(MAX), CONSTRAINT PK_NeutralTexts PRIMARY KEY CLUSTERED (Id))
CREATE FULLTEXT INDEX ON NeutralTexts (Text LANGUAGE 'Neutral') KEY INDEX PK_NeutralTexts ON ([TestSearchCatalog]) WITH (CHANGE_TRACKING = AUTO, STOPLIST = OFF)
INSERT INTO NeutralTexts(Text) VALUES ('PRFX 12233')
INSERT INTO NeutralTexts(Text) VALUES ('SUR 12233')
INSERT INTO NeutralTexts(Text) VALUES ('SCR 12233')

-- following query returns 1 row but should 3 - a possible bug in english word breaker
SELECT * FROM EnglishTexts WHERE CONTAINS(Text, '"12233"')

-- following query returns 2 rows but should 3 - neutral language word breaker is also treating SUR acronym specially - another bug?
SELECT * FROM NeutralTexts WHERE CONTAINS(Text, '"12233"')

-- following query returns 1 row but should 3 - forcing neutral language on a query on english index should apply neutral language (i might misunderstand if this is even possible without a neutral index)
SELECT * FROM EnglishTexts WHERE CONTAINS(Text, '"12233"', LANGUAGE 0)

-- following query returns 2 rows but should 3 - using neutral language on neutral language indexed table should not make a difference
SELECT * FROM NeutralTexts WHERE CONTAINS(Text, '"12233"', LANGUAGE 0)

-- for reference - English word breaker does not split SCR with 12233 and SUR with 12233, causing above problems
SELECT * FROM sys.dm_fts_parser ('"SCR 12233 SUR 12233"', 1033, 0, 0)

-- for reference - Neutral word breaker correctly splits SCR and 12233 but not SUR with 12233
SELECT * FROM sys.dm_fts_parser ('"SCR 12233 SUR 12233"', 0, 0, 0)

我检查你的案例很有趣。 经过一些工作和研发,我发现一些东西可能对你有帮助。

问题是 1 或 2 个字符是停用词,因此当用户搜索包含它的短语时,全文索引搜索引擎会跳过它。解决方案:

1) Drop all full text indexes and full text catalog

2)Create a stop list and set in the stoplist Action=Delete All Stopwords and Full-Text Language=English

3)Create all full text indexes and full text catalog through scripts as given below instead of wizard

4)Attach stoplist to the full text indexes when creating them


/*******************Drop and create FULL TEXT CATALOG for AbstractSearch*************************************/

/****** Drop:  FullTextCatalog [abstractSearch]    Script Date: 12/02/2011 13:10:21 ******/

GO

IF  EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[Abstract]'))

ALTER FULLTEXT INDEX ON [dbo].[Abstract] DISABLE

GO

/****** Object:  FullTextIndex     Script Date: 12/02/2011 13:10:21 ******/

IF  EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[Abstract]'))

DROP FULLTEXT INDEX ON [dbo].[Abstract]

GO

IF  EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[Author]'))

ALTER FULLTEXT INDEX ON [dbo].[Author] DISABLE

GO

/****** Object:  FullTextIndex     Script Date: 12/02/2011 13:10:21 ******/

IF  EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[Author]'))
DROP FULLTEXT INDEX ON [dbo].[Author]

GO

IF  EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[Synonym]'))
ALTER FULLTEXT INDEX ON [dbo].[Synonym] DISABLE

GO

/****** Object:  FullTextIndex     Script Date: 12/02/2011 13:10:21 ******/

IF  EXISTS (SELECT * FROM sys.fulltext_indexes fti WHERE fti.object_id = OBJECT_ID(N'[dbo].[Synonym]'))
DROP FULLTEXT INDEX ON [dbo].[Synonym]
GO

IF  EXISTS (SELECT * FROM sysfulltextcatalogs ftc WHERE ftc.name = N'abstractSearch')

DROP FULLTEXT CATALOG [abstractSearch]
GO


/****** Create:  FullTextCatalog [abstractSearch]    Script Date: 12/02/2011 13:10:21 ******/

CREATE FULLTEXT CATALOG [abstractSearch]

AS DEFAULT

GO

/****** Create:  FullTextIndex on  Abstract with stoplist set to custom stoplist   Script Date: 12/02/2011 13:10:21 ******/

CREATE FULLTEXT INDEX ON dbo.Abstract

(abstractTitle, abstractDescription)

KEY INDEX PK_Abstract
ON [abstractSearch]

WITH STOPLIST = [AbstractSearchStopList]


/******  Create:  FullTextIndex on  Synonym with stoplist set to custom stoplist    Script Date: 12/02/2011 13:10:21 ******/
CREATE FULLTEXT INDEX ON dbo.Synonym

(synonyms,keywordSynonym)

KEY INDEX PK_Synonyms

ON [abstractSearch]

WITH STOPLIST = [AbstractSearchStopList]


/******  Create:  FullTextIndex on  Author with stoplist set to custom stoplist   Script Date: 12/02/2011 13:10:21 ******/

CREATE FULLTEXT INDEX ON dbo.Author

(firstName,lastName,middleName)

KEY INDEX PK_Author

ON [abstractSearch]
WITH STOPLIST = [AbstractSearchStopList]

最后我能够确定问题与货币符号(显然 SUR 和 SCR 是货币符号)后跟或前面有一个数字有关,导致两者被索引在一起。

在我看来,只有当用户希望过去(SUR - 苏联卢布,自 1993 年以来未使用)或当前(SCR - 塞舌尔卢比)货币出现在文本中并且仅当货币根据标准符号在数字之后或之前(例如 $ 在数字之前,SCR 或 € 在数字之后)。

此外,货币符号似乎会部分影响中性语言中断 - 过去的货币如 SUR 很好,但考虑到语言中性文本处理不应受到任何词典的影响,影响语言中性分词的当前货币是完全出乎意料的行为单词。

Microsoft documentation of SQL Server 2012 及更高版本 FTS 文本处理解释了对分词器的相关更改,表明新的分词器不会单独索引货币符号或数字,即使在语言中性分词器:

term previous new
100$ 100$ 100$
100$ nn100 nn100usd
0 000 USD 0 0 000 usd
0 000 USD 000
0 000 USD nn000
0 000 USD nn100$
0 000 USD usd

解决原始问题的唯一解决方案是恢复到 2012 年之前的分词器和词干分析器 described here。该解决方案涉及更改以下注册表项的几个步骤(另存为 .reg 文件并打开以应用,适用于 SQL Server 2017 上的默认实例 - MSSQL14.MSSQLSERVER - 将其更改为您的实例目录名称C:\Program File\Microsoft SQL Server):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSearch\Language\enu]
"WBreakerClass"="{188D6CC5-CB03-4C01-912E-47D21295D77E}"
"StemmerClass"="{EEED4C20-7F1B-11CE-BE57-00AA0051FE20}"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSearch\CLSID\{188D6CC5-CB03-4C01-912E-47D21295D77E}]
@="langwrbk.dll"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSearch\CLSID\{EEED4C20-7F1B-11CE-BE57-00AA0051FE20}]
@="infosoft.dll"

更改注册表后,SQL 服务器需要重新启动并重新创建 FULLTEXT INDEX 对象 (DROP + CREATE FILLTEXT INDEX ON...) 才能使更改生效。

要恢复为原始分词器和词干分析器,请使用以下注册表项:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSearch\Language\enu]
"WBreakerClass"="{9FAED859-0B30-4434-AE65-412E14A16FB8}"
"StemmerClass"="{E1E5EF84-C4A6-4E50-8188-99AEF3DE2659}"

使用旧版本的分词器显然有缺点,但至少货币符号的索引与其周围的数值分开。

我想补充一点,我向 Microsoft 支持报告了这个问题,它最终被归类为预期和期望的行为,除了使用旧的分词器之外无法修复它。

SQL 服务器在处理像 SUR 这样的术语时缺乏灵活性,在我的域中它指的是 Surgery 而不是 塞舌尔卢比, 让我开始将我们的产品迁移到 PostgreSQL,将在接下来的 6 个月内完成。