原文:【SQL Server性能优化】运用SQL Server的全文检索来提高模糊匹配的效率


今天去面试,这个公司的业务需要模糊查询数据,之前他们通过mongodb来存储数据,但他们说会有丢数据的问题,我从业务上了解到,显然对他们公司而言,丢数是绝对不能允许的。

另外,他们说之前也用过SQL Server的全文检索,但速度不够快,不如用mongodb快,当然我不太清楚他们所谓快的具体定义,比如查询只需要1秒,还是1分钟。他们的系统现在采用的是SQL Server,通过复制来实现高可用性,因为他们说备份数据库需要很长时间。我在想,这确实解决了可用性问题,但没解决性能问题,可以考虑分表,把大的表拆分到多个数据库,每个数据库可以通过复制来实现可用性。

我觉得他们可能更需要一个架构师,来决定采用何种技术解决他们现在的技术问题,因为这种技术问题,显然不是靠DBA的优化就能解决的;其次才是需要DBA,让DBA来管理、维护、优化系统。

当时在面试的时候,我表示虽然在博客里也写了一篇关于SQL Server全文索引的文章,但在实际工作中确实也没有用到。回去以后,我想了想,觉得这个问题还是可以通过SQL Server的全文索引来尝试一下。

引用自己之前写的一篇全文检索的文章:   http://blog.csdn.net/yupeigu/article/details/7792955

上面的文章是去年写的,当时在看《SQL Server 2008 实战》这本书,看完后,觉得不能只是看书,不然很快就会忘记,于是在空闲的时候,把书上的东西实践了一下,算是装模做样的把书上的代码抄写了一遍,就算是实践过了,放心了。但其实很快就忘记了,就算抄写10遍,也会忘记,学了不用等于不学。不过这也没办法,因为学了这个全文检索,公司里也用不到。

现在回想一下,这种实践有点像以前小学和初中时抄写错别字一下,字写错了,老师会说:“你把这个句子抄写100遍”,一开始抄写的时候,还挺认真的,但写了一会,手就开始酸了。

于是手上握着5支笔,开始抄写,这样就能一次抄写5遍,效率提高了好多倍,现在想想这个是偷工减料,但也包含了优化的思想,那就是同时用更多的资源(这里是5支笔)来做事。

但再想想,其实这种学习效率其实是很差的,本质上就是做了不少的无用功,没必要抄写那么多遍,所以就有另一种优化,那就是少做无用之事,少做无用功。

言归正传,现在有这样一个问题,有个字段,文本型的,可能会有上万个文字,现在要从表中,通过这个字段的文本,找到复合要求的记录,那么从SQL Server数据库的角度,有什么方法呢?

我觉得通过使用全文检索,能少做不少的无用功。下面是例子。

一、首先是普通的方法:


  1. set statistics io on
  2. set statistics time on
  3. create database wc
  4. go
  5. use wc
  6. go
  7. create table tbl_word
  8. (
  9. i int not null primary key identity(1,1),
  10. v nvarchar(max) --存储大量文字
  11. )
  12. go
  13. --delete from tbl_word
  14. insert into tbl_word
  15. values('我的一个兴趣是看电影。'),
  16. ('我的一个爱好是看电影和电视剧')
  17. insert into tbl_word
  18. values(replicate('我的一个爱好是看电影和电视剧',1000) + '兴趣' +
  19. replicate('我的爱好是看电视剧和film和动漫',1500))
  20. go 1000
  21. insert into tbl_word
  22. values('我的一个兴趣是看电影。'),
  23. ('我的一个爱好是看电影和电视剧')
  24. go 100
  25. insert into tbl_word
  26. values(replicate('我的一个爱好是看电影和电视剧',1000) + 'haha' +
  27. replicate('我的爱好是看电视剧和film和动漫',1500))
  28. go
  29. /*
  30. SQL Server 分析和编译时间:
  31. CPU 时间 = 0 毫秒,占用时间 = 3 毫秒。
  32. (1 行受影响)
  33. 表 'tbl_word'。扫描计数 1,逻辑读取 1509 次,物理读取 0 次,预读 0 次,lob 逻辑读取 5 次,lob 物理读取 0 次,lob 预读 0 次。
  34. SQL Server 执行时间:
  35. CPU 时间 = 484 毫秒,占用时间 = 490 毫秒。
  36. */
  37. select *
  38. from tbl_word
  39. where v like '%haha%'

二、全文检索的方法:


  1. create fulltext catalog cat_production_document
  2. go
  3. --从系统干扰词表,来创建自定义的干扰词表,因为系统干扰词表是无法修改的
  4. CREATE FULLTEXT STOPLIST WCX
  5. from system stoplist;
  6. go
  7. create fulltext index on dbo.tbl_word --在这个表上建全文索引
  8. (
  9. v
  10. )
  11. key index PK__tbl_word__3BD019960BC6C43E --键索引,一般是表的主键,这里需要修改为具体的名称
  12. on cat_production_document --全文目录
  13. with (CHANGE_TRACKING AUTO, --全文索引会随着表数据的修改而自动更新
  14. StopList=wcx); --是用自定义的干扰字表
  15. go
  16. ALTER FULLTEXT INDEX ON dbo.tbl_word
  17. enable
  18. go
  19. set statistics io on
  20. set statistics time on
  21. --查询基于变形的,字面的,同义的匹配方式搜索全文列
  22. --会返回要搜索文本中包含的单词以及单词的同义词,变形词(复数)的记录
  23. /*
  24. SQL Server 分析和编译时间:
  25. CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
  26. SQL Server 执行时间:
  27. CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
  28. SQL Server 分析和编译时间:
  29. CPU 时间 = 0 毫秒,占用时间 = 5 毫秒。
  30. (1 行受影响)
  31. 表 'tbl_word'。扫描计数 0,逻辑读取 3 次,物理读取 0 次,预读 0 次,lob 逻辑读取 3 次,lob 物理读取 0 次,lob 预读 0 次。
  32. (1 行受影响)
  33. SQL Server 执行时间:
  34. CPU 时间 = 0 毫秒,占用时间 = 10 毫秒。
  35. SQL Server 分析和编译时间:
  36. CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
  37. SQL Server 执行时间:
  38. CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
  39. */
  40. SELECT *
  41. from dbo.tbl_word
  42. WHERE FREETEXT (v, --带全文索引的列名
  43. 'haha'); --要搜索的文本

从上面的2段代码在执行时所消耗的时间,就可以清楚的看出2种方法的效率差异,全文索引的效率提高了50倍左右。

其实采用合适的技术(比如,这里的全文检索技术,就很适合模糊查询),就能提高不少性能。

3、全文检索的问题

有时候,我们发现有些词是找不到的,这个主要是因为干扰词的影响,比如我们查询"是",那么就没有记录返回。

通过dm_fts_parser,我们可以知道sql server 的全文服务,是如何来断词分词的。


  1. --1. 但是如果我们查询"是",会发现没有返回记录
  2. SELECT *
  3. from dbo.tbl_word
  4. WHERE FREETEXT (v, --带全文索引的列名
  5. '是'); --要搜索的文本
  6. --2.查询地区标识
  7. select name,
  8. alias,
  9. lcid --地区标识符
  10. from sys.syslanguages
  11. where name = '简体中文'
  12. /*
  13. name alias lcid
  14. 简体中文 Simplified Chinese 2052
  15. */
  16. --3.干扰词列表id
  17. select *
  18. from sys.fulltext_stoplists
  19. /*
  20. stoplist_id name
  21. 5 WCX
  22. */
  23. --4.是否可以被全文引擎识别,也就是对内容,断词后的结果
  24. --"是"是一个Noise Word ,也就是干扰词
  25. select special_term,
  26. display_term,
  27. source_term
  28. from sys.dm_fts_parser
  29. ('我的一个兴趣是看电影。', --要搜索的字符串
  30. 2052, --地区标识符
  31. 5, --干扰词列表id
  32. 0) --是否区分重音
  33. /*
  34. special_term display_term source_term
  35. Noise Word 我 我的一个兴趣是看电影。
  36. Noise Word 的 我的一个兴趣是看电影。
  37. Noise Word 一 我的一个兴趣是看电影。
  38. Noise Word 个 我的一个兴趣是看电影。
  39. Exact Match 兴趣 我的一个兴趣是看电影。
  40. Noise Word 是 我的一个兴趣是看电影。
  41. Exact Match 看 我的一个兴趣是看电影。
  42. Exact Match 电影 我的一个兴趣是看电影。
  43. */
  44. --5.1 我们可以查询一下干扰词列表,发现 "是" 是一个干扰词
  45. SELECT stopword
  46. FROM sys.fulltext_stopwords
  47. WHERE language_id = 2052 and stopword = '是'
  48. /*
  49. stopword
  50. */
  51. --5.2把干扰词"是"去掉
  52. ALTER FULLTEXT STOPLIST wcx
  53. DROP '是' language 'Simplified Chinese';
  54. --5.3再次查询,没有结果返回,发现已去掉这个干扰词
  55. SELECT stopword
  56. FROM sys.fulltext_stopwords
  57. WHERE language_id = 2052 and stopword = '是'
  58. --5.4 会返回3000多条记录
  59. SELECT *
  60. from dbo.tbl_word
  61. WHERE FREETEXT (v, --带全文索引的列名
  62. '是'); --要搜索的文本
  63. --5.5 但考虑到这个"是"没什么意义,所以还是需要加到干扰词列表中
  64. ALTER FULLTEXT STOPLIST wcx
  65. add '是' language 'Simplified Chinese';
  66. --5.6 再次查询,没有返回任何记录,说明干扰词起作用了
  67. SELECT *
  68. from dbo.tbl_word
  69. WHERE FREETEXT (v, --带全文索引的列名
  70. '是'); --要搜索的文本
  71. --6.1 改成使用系统干扰词列表
  72. ALTER FULLTEXT INDEX on tbl_word --表名
  73. SET STOPLIST=system ;--指定使用的全文非索引字表为系统干扰词列表
  74. --6.2 启动填充,如果CHANGE_TRACKING != AUTO,则需要启动一次填充才使新设定的全文非索引字表生效;
  75. ALTER FULLTEXT INDEX on tbl_word --表名
  76. START FULL POPULATION

有的时候,我们要查询的单词,虽然不是Noise Word,但还是会查询不到,这个就是全文检索的分词的问题了

对于每种断字符语言,断词结果是无法改变的。如果实在想要改变,只能通过微软公布的接口,自行编程修改相应的组件。

不过我们可以通过修改 分词所使用的语言来尝试一下,比如一般是用2052,也就是简体中文,如果用1028,也就是繁体中文,那么就有可能达到合理分词的目的。

发布了416 篇原创文章 · 获赞 135 · 访问量 94万+

【SQL Server性能优化】运用SQL Server的全文检索来提高模糊匹配的效率的更多相关文章

  1. 神奇的 SQL 之性能优化 → 让 SQL 飞起来

    开心一刻 一天,一个男人去未婚妻家玩,晚上临走时下起了大雨 未婚妻劝他留下来过夜,说完便去准备被褥,准备就绪后发现未婚夫不见了 过了好久,全身淋的像只落汤鸡的未婚夫回来了 未婚妻吃惊的问:" ...

  2. 【SQL Server性能优化】SQL Server 2008该表压缩

    当数据库是比较大的,而当你想备份,我们可以启动数据库备份压缩.这项由于备份文件比较小的压缩,所以整个备份的更快的速度,同时还低了磁盘空间的消耗. 当然还有一方面.肯定会添加cpu的消耗.只是一般的se ...

  3. SQL Server数据库性能优化之SQL语句篇【转】

    SQL Server数据库性能优化之SQL语句篇http://www.blogjava.net/allen-zhe/archive/2010/07/23/326927.html 近期项目需要, 做了一 ...

  4. SQL Server 性能优化(一)——简介

    原文:SQL Server 性能优化(一)--简介 一.性能优化的理由: 听起来有点多余,但是还是详细说一下: 1.节省成本:这里的成本不一定是钱,但是基本上可以变相认为是节省钱.性能上去了,本来要投 ...

  5. SQL Server 性能优化之——系统化方法提高性能

    SQL Server 性能优化之——系统化方法提高性能 阅读导航 1. 概述 2. 规范逻辑数据库设计 3. 使用高效索引设计 4. 使用高效的查询设计 5. 使用技术分析低性能 6. 总结 1. 概 ...

  6. SQL Server性能优化与管理的艺术 附件下载地址

    首先感谢读者们对鄙人的支持,购买了<SQL Server性能优化与管理的艺术>,由于之前出版社的一些疏忽,附件没有上传成功,再次本人深表歉意. 请需要下载附件的读者从下面链接下载,谢谢: ...

  7. Dynamics AX 2012 性能优化之 SQL Server 复制

    Dynamics AX 2012 性能优化之 SQL Server 复制 分析数据滞后 在博文 Dynamics AX 2012 在BI分析中建立数据仓库的必要性 里,Reinhard 阐述了在 AX ...

  8. SQL Server性能优化(6)查询语句建议

    1. 如果对数据不是工业级的访问(允许脏读),在select里添加 with(nolock) ID FROM Measure_heat WITH (nolock) 2. 限制结果集的数据量,如使用TO ...

  9. SQL SERVER性能优化综述

    SQL SERVER性能优化综述 一个系统的性能的提高,不单单是试运行或者维护阶段的性能调优的任务,也不单单是开发阶段的事情,而是在整个软件生命周期都需要注意,进行有效工作才能达到的.所以我希望按照软 ...

随机推荐

  1. SQL中如何使用方向键——lrwrap

    Linux alias命令用于设置指令的别名. 用户可利用alias,自定指令的别名.若仅输入alias,则可列出目前所有的别名设置.alias的效力仅及于该次登入的操作.若要每次登入是即自动设好别名 ...

  2. Row_Number() and Rank() in SQL

    1. 数据表实例数据 2. 使用Row_Number()方法给每一行数据添加一个唯一编号, 可以按照某一列进行排序. 3. 使用Partition by Column在一个Partition内进行编号 ...

  3. osg create shape

    osg::ref_ptr<osg::Node> OSG_Qt_::createSimple() { osg::ref_ptr<osg::Geode> geode = new o ...

  4. python 中 staticmethod 和 classmethod有什么区别

    面试中经常会问到staticmethod 和 classmethod有什么区别? 首先看下官方的解释: staticmethod: class staticmethod staticmethod(fu ...

  5. OpenStack Magnum项目简介

    1 项目简介 Magnum是OpenStack中一个提供容器集群部署的服务. Magnum是一个Pass层的OpenStack项目. Magnum使用Heat部署一个包含Docker和Kubernet ...

  6. Hibernatne 缓存中二级缓存简单介绍

    hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了. 二级缓存是Sessio ...

  7. TEC-2机微程序设计

    了解TEC-2机的结构和基本的指令知识很重要,不理解而只知一味地照抄是学不到知识的.建议先阅读课件,再结合例子进行理解.以下例子只供参考,有些地方可以合并,具体的操作仍需见仁见智.理解并学会使用微指令 ...

  8. 获取网卡名称及其IP地址的方法

    代码 # -*- coding: utf-8 -*- import psutil #获取网卡名称和其ip地址,不包括回环 def get_netcard(): netcard_info = [] in ...

  9. 转:TSDF in Kinect fusion

    KinectFusion中用到的TSDF Fusion 原po:https://blog.csdn.net/qq_31785865/article/details/78524429 最近在看关于稠密三 ...

  10. Time & Space Complexity

    Quick Sort: Time complexity: best case O(n*lgn), worst case O(n^2) Space complexity: Best case O(lgn ...