sql server全文索引使用中的小坑 (转载)
一、业务场景
我们在实际生产环境中遇到了这样一种需求,即需要检索一个父子关系的子树数据
估计大家也遇到过类似的场景,最典型的就是省市数据,其中path字段是按层级关系生成的行政区路径:
如果我们已知某市名,想查出同级和高一级的省名,如保定市同级和上级河北省,那么我们有什么实现方式呢
给大家10秒钟,快速抢答
.
.
.
时间到
大概有以下几种方法:
a.2008中新添加的层级数据类型
b.cte递归方式
c.直接程序处理
d.比较傻的方式:全文索引。。。
层级关系是实现这种业务逻辑的最好方式,比较经典的方式是递归方式,但是,我们不讲这两个方法内容和实现,因为今天的主题是:全文索引中的坑,因为这是我实实在在遇到的案例
我们的业务逻辑是这样的,已知一个或者几个ID,但并不了解这个ID的层次关系,需要的数据是提取对应ID的所有层次,比如按上图来说,我传入id in(9,910)那么就要所有河北省的数据,如果我传入id in(910),那么所取的数据就是保定市以及下属所有行政区
二、问题出现
为了简化说明问题,我把数据精简为表pathtest,数据为下图形式
按照上段说明的业务需求,在path需要建立一个全文索引方便检索,这个过程相信大家都知道如何建,就不描写过程了
我们先看一下实际包含66的数据行有多少?(或者说ID=66及以下的子树数据集)
select * from pathtest where path like '%,66,%'
(49 行受影响)
结果省略一部分,但可以看出是49行
之后我运行如下的查询,查询路径下包含66的数据集,也就是ID=66以及之下的子树
select * from pathtest where freetext(path,'"66"')
(17 行受影响)
显示是17行结果??
差这么多啊?就说是全文索引不精确,这也差太多了吧
OK,我们继续
select * from pathtest where freetext(path,'"66,480"')
同样,按逻辑上说,这个查询同样是检索66层次ID下的子树(PS:这里480是在66子树下)
但是:
这下更方便看了,根本就没有数据,这是怎么回事?
三、问题研究
有些经验的同学肯定已经想到这是全文索引建立的时候分词出现了问题,那么我们就看一下
sys.dm_fts_parser 这个函数是对字符串按全文索引方式进行关键字拆分,具体使用请查阅MSDN
我分别取两个路径串
1.path=’,66,73,’
2.path=’,66,480,’
我们来看一下拆分的结果
select * from sys.dm_fts_parser ('",66,73,"',1033,0,1)
嗯,把66,73分别拆分成字符串关键字“66”、“73”以及数值(nn开头)的66、73一共四条记录,没有问题
select * from sys.dm_fts_parser ('",66,480,"',1033,0,1)
这下问题出现了,字符串并没有按我们想的那样拆分成“66”与“480”的关键字,而是生成了”66,480”与66480的两个关键字
所以我们在进行freetext(path,'"66,480"')检索的时候返回了一条满足条件的记录,且freetext(path,'"480"')的时候没有满足关键字的记录。
这里涉及到一个全文索引关键词拆分算法的问题,sqlserver把类似66,480这种字符串当成了有千分位分隔符的纯数字,所以不再进行拆分
但是像66,73这种字符串,不是数字的千分位分隔表示方法,所以进行了拆分
假如我们把66,480这个字符串改写成66,480,1,则会进行字符串方式的拆分,如下:
select * from sys.dm_fts_parser ('",66,480,1,"',1033,0,1)
同时我们知道freetext这个函数也是以建立全文索引的方式进行检索词拆分后与全文索引进行比较,所以在进行where freetext(path,'"66,480"')检索的时候,也是把字符串进行了处理,但同样是将字符串以千分位分隔数字进行了处理,所以可以检索出一条path=’66,480’记录,但是在进行where freetext(path,'"480"')的时候,由于全表的全文索引中,并没有拆分出480这个字符串关键词,所以没有满足条件的记录
如果我们将检索串改写为where freetext(path,'"66 480"'),那么对于这个逻辑来说,就是要检索全文索引中满足66或者480关键字的结果集,在本例中,结果集为17条,即与where freetext(path,'"66"')相同(因为空格符做为了断字符将字符拆为了“66”与“480”,表中满足66的结果集为17条,480为0条,所以结果总数为17条)
四、解决方案
既然知道了是”,”引起的字符串误判断,我们就把这个替换掉
update pathtest set path=replace(path,',',';')
等全文索引重新填充完成后,执行
select * from pathtest where freetext(path,'"66"')
(49 行受影响)
好!与直接like的结果一致
之后我们再运行 select * from pathtest where freetext(path,'"66,480"')
(0 行受影响)
!!!什么情况 !?
之前我们说了,“66,480”会被误判断拆分,所以这里的检索词也不能这么写了,可以写空格,也可以写分号,反正就是不能用逗号,将查询改为
select * from pathtest where freetext(path,'"66 480"')
(49 行受影响)
select * from pathtest where freetext(path,'"480"')
(1 行受影响)
好了,这下与期待结果一致了
五、后记
这个问题解决前还出过一段插曲,把“,”替换成了“/”,但是在进行拆分的时候/66/480/拆分成了”66”与”480/”,后来将/加入了stoplist,这个问题就解决掉了,本次测试我没有再现这个情况,应该是当时设置的断字语言导致的,有兴趣的同学可以自己玩一下。
切记如果你要建的全文索引中有类似的字段,清注意逗号问题,同时在freetext检索的时候,也同样要注意“,”的问题。
此外我们看到SqlServer的全文索引,实际上就是命中查询条件中搜索的关键字来筛选数据的,SqlServer将查询语句中搜索的关键字和全文目录中收集的关键字进行匹配,返回数据表中关键字匹配成功的数据行。但是从本文可以看到这种匹配全文目录关键字的查找,并不是像like关键字一样100%返回满足条件的数据,如果全文目录中收集的关键字不够多或不正确,全文索引的匹配就会失败,导致查询结果不正确,所以在SqlServer中使用全文索引一定要小心谨慎!
sql server全文索引使用中的小坑 (转载)的更多相关文章
- sql server全文索引使用中的小坑
一.业务场景 我们在实际生产环境中遇到了这样一种需求,即需要检索一个父子关系的子树数据 估计大家也遇到过类似的场景,最典型的就是省市数据,其中path字段是按层级关系生成的行政区路径: 如果我们已知某 ...
- SQL Server 全文索引的硬伤(转载)
本文关键字:SQL Server全文索引.CONTAINS.FREETEXT.CONTAINSTABLE.FREETEXTTABLE等谓词. 想象这样一个场景:在DataBase_name.dbo.T ...
- SQL Server还原和一些小发现
1.当数据库已经建好在DB里,SQL Server 2005 和 SQL Server 2008使用.Bak文件还原的时候,会出现 error : "The backup set holds ...
- SQL Server 全文索引的硬伤
本文关键字:SQL Server全文索引.CONTAINS.FREETEXT.CONTAINSTABLE.FREETEXTTABLE等谓词. 想象这样一个场景:在DataBase_name.dbo.T ...
- SQL Server 全文索引介绍(转载)
概述 全文引擎使用全文索引中的信息来编译可快速搜索表中的特定词或词组的全文查询.全文索引将有关重要的词及其位置的信息存储在数据库表的一列或多列中.全文索引是一种特殊类型的基于标记的功能性索引,它是由 ...
- SQL Server 全文索引创建
在安装数据库管理系统SQL Server 后,默认情况下全文索引的服务是没有开启的 ,所以首先需要先开启服务,在sql server配置管理器中 (sql server configuration M ...
- 使用Source Safe for SQL Server解决数据库版本管理问题(转载)
简介 在软件开发过程中,版本控制是一个广为人知的概念.因为一个项目可能会需要不同角色人员的参与,通过使用版本控制软件,可以使得项目中不同角色的人并行参与到项目当中.源代码控制使得代码可以存在多个版本, ...
- sql server 常用的函数小汇
摘录些许sqlserver 常用到的一些函数,便于日常学习使用 一.字符转换函数1.ASCII()返回字符表达式最左端字符的ASCII 码值.在ASCII()函数中,纯数字的字符串可不用‘’括起来,但 ...
- SQL Server 全文索引的管理
全文索引不同于常见的聚集索引或非聚集索引,这些索引的内部实现是平衡树(B-Tree)结构,而全文索引在物理上是由一系列的内部表(Internal tables)构成的,这些内部表称作全文索引片段(Fr ...
随机推荐
- Kaggle-tiantic数据建模与分析
1.数据可视化 kaggle中数据解释:https://www.kaggle.com/c/titanic/data 数据形式: 读取数据,并显示数据信息 data_train = pd.read_cs ...
- sublime text 前端开发插件安装和配置
前端开发sublimeconfig mac配置 此文件目录中文件主要是关于sublime的插件配置,快捷键配置,主题和字体配置. 插件列表 所有插件都可以使用Package Control安装,具体的 ...
- 829. 连续整数求和-leetcode
题目:给定一个正整数 N,试求有多少组连续正整数满足所有数字之和为 N? 示例 1: 输入: 5 输出: 2 解释: 5 = 5 = 2 + 3,共有两组连续整数([5],[2,3])求和后为 5. ...
- nginx介绍 - 部署到linux中
前言: tomcat理论并发处理能力, 大概500左右吧, 即使通过一些优化, 能提升一点, 但是, 并不能达到质变, 最多涨几百. 对于非互联网项目, 确实够用了. 在企业中, 如果要达到500并发 ...
- 实验吧 这个看起来有点简单!&渗透测试工具sqlmap基础教程
题目地址:http://ctf5.shiyanbar.com/8/index.php?id=1 下载sqlmap,拖到python安装文件夹下面,在桌面创建sqlmap的cmd快捷方式,都不赘述. 教 ...
- Deep learning with Python 学习笔记(8)
Keras 函数式编程 利用 Keras 函数式 API,你可以构建类图(graph-like)模型.在不同的输入之间共享某一层,并且还可以像使用 Python 函数一样使用 Keras 模型.Ker ...
- Spark ML机器学习
Spark提供了常用机器学习算法的实现, 封装于spark.ml和spark.mllib中. spark.mllib是基于RDD的机器学习库, spark.ml是基于DataFrame的机器学习库. ...
- Linux中ls命令用法
ls 命令的含义是list显示当前目录中的文件名字.注意不加参数它显示除隐藏文件外的所有文件及目录的名字. 1)ls –a 显示当前目录中的所有文件,包含隐藏文件 命令: aijian.shi@U-a ...
- IdentityServer4 中文文档 -3- (简介)已支持的规范
IdentityServer4 中文文档 -3- (简介)已支持的规范 原文:http://docs.identityserver.io/en/release/intro/specs.html 目 录 ...
- struts2、hibernate以及spring是如何配置的
第一次使用这3大框架进行网站编写的人没有一个清楚的流程,建起网站来会比较头疼,今天来讲讲3大框架的配置 基本流程: 用户点击页面之后,服务器收到一个请求,请求经过web.xml的拦截器过滤后进入act ...