一、业务场景

我们在实际生产环境中遇到了这样一种需求,即需要检索一个父子关系的子树数据

估计大家也遇到过类似的场景,最典型的就是省市数据,其中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子树下)

但是:

1行???这是什么情况

我们干脆再检索一下ID=480的数据吧

select * from pathtest where freetext(path,'"480"')

这下更方便看了,根本就没有数据,这是怎么回事?

三、问题研究

有些经验的同学肯定已经想到这是全文索引建立的时候分词出现了问题,那么我们就看一下

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文章,请关注微信号:insidesqlserver 或者扫描二维码

sql server全文索引使用中的小坑的更多相关文章

  1. sql server全文索引使用中的小坑 (转载)

    一.业务场景 我们在实际生产环境中遇到了这样一种需求,即需要检索一个父子关系的子树数据 估计大家也遇到过类似的场景,最典型的就是省市数据,其中path字段是按层级关系生成的行政区路径: 如果我们已知某 ...

  2. SQL Server 全文索引的硬伤(转载)

    本文关键字:SQL Server全文索引.CONTAINS.FREETEXT.CONTAINSTABLE.FREETEXTTABLE等谓词. 想象这样一个场景:在DataBase_name.dbo.T ...

  3. SQL Server 全文索引的硬伤

    本文关键字:SQL Server全文索引.CONTAINS.FREETEXT.CONTAINSTABLE.FREETEXTTABLE等谓词. 想象这样一个场景:在DataBase_name.dbo.T ...

  4. SQL Server 全文索引创建

    在安装数据库管理系统SQL Server 后,默认情况下全文索引的服务是没有开启的 ,所以首先需要先开启服务,在sql server配置管理器中 (sql server configuration M ...

  5. sql server 常用的函数小汇

    摘录些许sqlserver 常用到的一些函数,便于日常学习使用 一.字符转换函数1.ASCII()返回字符表达式最左端字符的ASCII 码值.在ASCII()函数中,纯数字的字符串可不用‘’括起来,但 ...

  6. SQL Server 全文索引的管理

    全文索引不同于常见的聚集索引或非聚集索引,这些索引的内部实现是平衡树(B-Tree)结构,而全文索引在物理上是由一系列的内部表(Internal tables)构成的,这些内部表称作全文索引片段(Fr ...

  7. SQL Server 全文索引介绍(转载)

    概述 全文引擎使用全文索引中的信息来编译可快速搜索表中的特定词或词组的全文查询.全文索引将有关重要的词及其位置的信息存储在数据库表的一列或多列中.全文索引是一种特殊类型的基于标记的功能性索引,它是由 ...

  8. SQL Server还原和一些小发现

    1.当数据库已经建好在DB里,SQL Server 2005 和 SQL Server 2008使用.Bak文件还原的时候,会出现 error : "The backup set holds ...

  9. MySQL基本语法(一):和SQL Server语法的差异小归纳

    html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...

随机推荐

  1. SQL Server安全(11/11):审核(Auditing)

    在保密你的服务器和数据,防备当前复杂的攻击,SQL Server有你需要的一切.但在你能有效使用这些安全功能前,你需要理解你面对的威胁和一些基本的安全概念.这篇文章提供了基础,因此你可以对SQL Se ...

  2. 表单多文件上传样式美化 && 支持选中文件后删除相关项

    开发中会经常涉及到文件上传的需求,根据业务不同的需求,有不同的文件上传情况. 有简单的单文件上传,有多文件上传,因浏览器原生的文件上传样式及功能的支持度不算太高,很多时候我们会对样式进行美化,对功能进 ...

  3. CSS垂直居中和水平居中

    前言 CSS居中一直是一个比较敏感的话题,为了以后开发的方便,楼主觉得确实需要总结一下了,总的来说,居中问题分为垂直居中和水平居中,实际上水平居中是很简单的,但垂直居中的方式和方法就千奇百怪了. 内联 ...

  4. C#不对称加密

    对称加密的缺点是双方使用相同的密钥和IV进行加密.解密.由于接收方必须知道密钥和IV才能解密数据,因此发送方需要先将密钥和IV传递给接收方.这就 有一个问题,如果攻击者截获了密钥和IV,也就等于知道了 ...

  5. luogg_java学习_12_线程

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! 线程 程序.进程.线程的概念 程序:我们用程序设计语言 ...

  6. Java IO之字节流

    Java中的输入是指从数据源等读到Java程序中,这里的数据源可以是文件,内存或网络连接,输出则是指从Java程序中写到目的地. 输入输出流可以分为以下几种类型(暂时不考虑File类) 类名 中文名 ...

  7. ABP中使用Redis Cache(2)

    上一篇讲解了如何在ABP中使用Redis Cache,虽然能够正常的访问Redis,但是Redis里的信息无法同步更新.本文将讲解如何实现Redis Cache与实体同步更新.要实现数据的同步更新,我 ...

  8. [deviceone开发]-日程日历示例

    一.简介 用户Wang利用Gridview和其它组件绘制的日历和任务,基本实现一个完整的在线日程管理功能.另外还封装了很多js对象,非常值得参考学习.二.效果图 三.相关下载 https://gith ...

  9. arcgis for flex展示GIS基本功能

    1.地图框选搜索: 这是空间查询,在地图上框选一定的范围,然后搜索出在这个范围之内的所有信息,搜索到的详细信息在列表框显示出来 2.属性查询: 3.数据库展示: 4.绘制图形: 地图上绘制各种不同形状 ...

  10. UIPickerView的使用(二)

    上篇文章 UIPickerView的使用(一)学习了如何创建单列选择器,现在看一下如何创建多列选择器 多列选择器(以二列为例) 1.遵守协议和创建两个数据源 2.创建pickView 3.实现代理 / ...