如何在SQL Server/Azure SQL中获取单词的词形变化形式并按词形组聚合全文索引关键词统计?
如何在SQL Server/Azure SQL中获取单词的词形变化形式并按词形组聚合全文索引关键词统计?
这个需求我之前做全文索引分析的时候也碰到过,确实SQL Server没有直接给一个开箱即用的函数来返回某个词的所有屈折形式,但咱们可以通过几个实用的技巧实现,而且能完美完成你要的按屈折组聚合统计的目标,一步步来:
一、先搞定:如何获取单个单词的所有屈折形式?
SQL Server的全文引擎有个隐藏的“神器”——sys.dm_fts_parser动态管理函数,它能调用全文分析器解析任意查询字符串,包括屈折形式的查询。用它就能轻松拿到某个词的所有屈折变化。
比如要获取“swim”的所有屈折形式,直接执行:
SELECT DISTINCT display_term FROM sys.dm_fts_parser('FORMSOF(INFLECTIONAL, "swim")', 1033, NULL, 0) -- 过滤掉英语噪音词(比如the、a这类无意义的词) WHERE display_term NOT IN (SELECT word FROM sys.fulltext_stopwords WHERE language_id = 1033)
执行后会返回你预期的结果:swim、swam、swum、swimming。
参数小科普:
1033是英语的LCID(语言代码),如果你的全文索引用的是其他语言,比如中文换成2052,可以查sys.fulltext_languages看所有支持的语言;- 第三个参数填
NULL表示不启用同义词库,要是你有自定义同义词库可以传它的ID; - 第四个参数
0表示不忽略噪音词,方便我们手动过滤掉不需要的词。
二、核心目标:按屈折组聚合全文索引的统计数
你要把sys.dm_fts_index_keywords里同属一个屈折组的词的document_count汇总,这个可以通过临时表+关联分析来实现,步骤很清晰:
步骤1:导出全文索引的关键词数据
先把全文索引里的关键词和对应文档数导出到临时表,方便后续操作:
-- 记得替换成你的带全文索引的表名 DECLARE @TargetTable NVARCHAR(128) = 'YourFulltextIndexedTable' SELECT display_term, document_count INTO #FtsKeywords FROM sys.dm_fts_index_keywords(DB_ID(), OBJECT_ID(@TargetTable)) WHERE display_term NOT LIKE '##%' -- 排除系统内置的特殊二进制词 AND document_count > 0 -- 去掉没有对应文档的无效词
步骤2:生成每个词的屈折组映射
通过CROSS APPLY调用sys.dm_fts_parser,为每个词匹配它所属的屈折组里的所有词,而且只保留我们索引里实际存在的词(避免出现索引里没有的无效词):
SELECT k.display_term AS source_term, p.display_term AS inflectional_term INTO #InflectionalGroups FROM #FtsKeywords k CROSS APPLY sys.dm_fts_parser( -- 替换词里的双引号,避免解析出错 'FORMSOF(INFLECTIONAL, "' + REPLACE(k.display_term, '"', '""') + '")', 1033, NULL, 0 ) p WHERE p.display_term IN (SELECT display_term FROM #FtsKeywords) AND p.display_term NOT IN (SELECT word FROM sys.fulltext_stopwords WHERE language_id = 1033)
步骤3:按屈折组聚合统计
现在我们要为每个屈折组选一个“代表词”(比如组里字典序最小的词),然后把同组所有词的文档数加起来:
WITH GroupedTerms AS ( SELECT ig.source_term, ig.inflectional_term, -- 选组里字典序最小的词作为组名,也可以换成你喜欢的其他规则 MIN(ig.inflectional_term) OVER (PARTITION BY ig.source_term) AS group_representative FROM #InflectionalGroups ig ) SELECT group_representative AS display_term_group, SUM(k.document_count) AS total_document_count FROM GroupedTerms gt JOIN #FtsKeywords k ON gt.source_term = k.display_term GROUP BY group_representative ORDER BY total_document_count DESC
执行后你会得到类似这样的结果:
| display_term_group | total_document_count |
|---|---|
| limit | 4062 |
| limited | 1396994 |
| limitation | 1135 |
(小提醒:这里limit和limited可能分属不同组,因为英语的词干分析器会区分动词和形容词的屈折形式,如果需要把派生形式比如limit和limitation也聚合,就得靠自定义同义词库来配置了)
最后别忘了清理临时表
DROP TABLE IF EXISTS #FtsKeywords DROP TABLE IF EXISTS #InflectionalGroups
三、几个关键注意事项
- 语言要匹配:一定要保证用的LCID和你的全文索引语言一致,不然词干分析结果会出错,比如你用了中文全文索引却传1033(英语),结果肯定不对;
- 性能优化:如果你的全文索引特别大,临时表会占不少资源,可以先过滤掉低频词(比如
document_count < 10),或者分批处理; - 屈折形式 vs 派生形式:屈折形式是语法变化(比如动词时态、名词单复数),派生形式是加前缀后缀生成的新词(比如
swim→swimmer),全文引擎不会自动把派生形式归为同一组,要聚合派生形式得自己做同义词库; - 噪音词过滤:一定要记得过滤噪音词,不然统计结果会被
the、a这类无意义的词占满。
这样就完全实现了你的需求,既能单独查某个词的屈折形式,又能按屈折组聚合统计全文索引的关键词数据啦!




