Sql Server中的全文索引(下面统一使用FULLTEXT INDEX来表示全文索引),是一种特定语言搜索索引功能。它和LIKE的不同,LIKE主要是根据搜索模板搜索数据,它的效率比FULLTEXT INDEX要低。在几百万的字符串中,LIKE须要花几分钟才能返回的结果,FULLTEXT INDEX可能只须要几秒钟。前端
FULLTEXT INDEX功能是Sql Server的可选项。你能够经过 SELECT FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') 命令,来检查你是否安装了FULLTEXT INDEX。sql
在开始介绍FULLTEXT INDEX以前,还须要介绍几个重要的相关概念。它们分别是catalog, fulltext index fragment, 数据库
Catalog: 在建立FULLTEXT INDEX以前,都须要先建立一个CATALOG, CATALOG是一个虚拟容器对象,它就是用来存储FULLTEXT Index的,一个CATALOG能够关联多个FULLTEXT Index索引。CATALOG不属于任何文件组。架构
FULLTEXT Index Fragments: 一般一个FULLTEXT索引都是由多个内部表组成的,这些内部表就被称为Fragments。当用户更新表中的数据后,数据库会对改变的部分自动建立一个Fragment对象(前提是在这个表上创建了FULLTEXT INDEX,而且设置了自动跟踪改变)。app
你可经过sys.fulltext_index_fragments表来查询全部fragments记录:运维
SELECT * FROM sys.fulltext_index_fragments;
输出:函数
经过上面的输出结果,能够看出一个表有了两个fragment对象。若是像这样的fragment对象愈来愈多的话,是会影响FULTEXT INDEX的查询效率的。为了减小Fragment的数量,可使用ALTER命令将全部的Fragment整合到一块儿。学习
ALTER FULLTEXT CATALOG [your-catalog-name] REORGANIZE;
建立测试表:测试
create table Text_Test_Table( id int not null IDENTITY(1,1) PRIMARY KEY, txt nvarchar(1000) null );
建立CATALOG:优化
CREATE FULLTEXT CATALOG my_catalog;
建立全文索引:
CREATE FULLTEXT INDEX ON Text_Test_Table ( txt --Full-text index column name Language 2057 --2057 is the LCID for British English ) KEY INDEX PK__Text_Tes__3213E83F0F8DA0F2 ON my_catalog --Unique index WITH CHANGE_TRACKING AUTO --Population type; GO
上面的表中指定了全文索引的字段为txt,同时也指定了它的语言为英式英语。你也能够指定为其它语言,在SQL SERVER中用 select * from sys.fulltext_languages; 能够查询全部支持的语言编号。为字段指定正确的语言编号是必需的,由于不一样的语言编号会使用不一样的单词分割器,不一样的单词分割器会分割出不一样的关键词列表,当查询的时候就须要匹配关键词列表中的数据。
建立全文索引,须要当前表至少包含一个惟一索引(unique index)。上面案例中, PK__Text_Tes__3213E83F0F8DA0F2 是主键,知足惟一性的要求,所以能够用来建立全文索引。
CHANGE_TRACKING AUTO:是指当表中数据有变化时,会自动更新全文索引中的记录。(除了AUTO,还能够为MANUAL值,表示须要手动去更新全文索引的记录)。
其实这个比较好理解,能够把全文索引理解为一张 关键词与数据表的关系对照表。只有数据表上有一个Unique索引时,全文索引才能够惟一地关联关键词与数据表之间的关系。
有点绕?直接上代码!
--插入测试数据 insert into Text_Test_Table values('She''s great fun, but she''s a few sandwiches short of a picnic.'); insert into Text_Test_Table values('She''s funny, but she only eat a few sandwiches.'); --查询数据表 select * from Text_Test_Table; --查询全文索引关键词对照关系, --数据库为Test,数据表为Text_Test_Table SELECT * FROM sys.dm_fts_index_keywords_by_document(db_id('Test'), object_id('dbo.Text_Test_Table'))
效果对照图:
索引表中的document_id对应的是Text_Test_Table表中的id字段。display_term是关键词。occurence_count是关键词的出现次数。document_id就是对应了数据表中惟一约束数据的编号,当惟一约束做用在数字字段上时,约束字段的值就会和document_id同样。若是惟一约束做用在字符字段上的话,那么document_id将会是对应数据的编号。
若是要彻底匹配查询的关键字,那么能够用contains函数。
语法:
CONTAINS(字段,关键词)
关键词须要用冒号""括起来,多个关键词用空格隔开。格式:"关键词1 关键词2 关键词3..."。contains中的关键词就至关于AND关键词,这些关键词并不须要连在一块儿,可是须要全部的关键词都存在。
案例:
-- 查询Text_Test_Table表中txt字段包含 a 和 few 和 sandwiches 的关键词的全部数据 declare @terms nvarchar(1000) = '"a few sandwiches"'; select * from Text_Test_Table where contains(txt, @terms); -- 能够查询出: -- she has a few sandwiches -- a few lemon and sandwiches -- a man and few person both have sandwiches
若是要包含全部的关键词,那么应使用FREETEXT函数。
语法:
FREETEXT(字段, 关键词)
关键词须要用冒号""括起来,多个关键词用空格隔开。格式:"关键词1 关键词2 关键词3..."。freetext中的关键词就至关于OR关联词,这些关键词并不须要连在一块儿,只要有一个关键词存在数据就会被选取。
-- 查询Text_Test_Table表中txt字段包含 a 或 few 或 sandwiches 的关键词的全部数据 declare @terms nvarchar(1000) = '"a few sandwiches"'; select * from Text_Test_Table where freetext(txt, @terms); -- 能够查询出: -- she has a few sandwiches -- sandwiches is good -- There are few apples
注意:
Freetext函数还能够匹配关键词的变体,好比经过关键词catch匹配相应的变体caught, catching, 和 catches 等。但Contains函数想要查询变体,就必需使用FORMSOF语句。请移步FORMSOF Predicate获取更详细的信息。
有一张products表,其中有id,name和description字段, id是主键。name 和 description是nvarchar字段类型。如今要查询Products中的数据,name必需要包含全部的查询关键字,description至少须要匹配一个提供的关键词。
create table Products( id int not null IDENTITY(1,1) PRIMARY KEY, [name] nvarchar(1000) null, [description] nvarchar(1000) null ); CREATE FULLTEXT INDEX ON Products ( [name] Language 2057, [description] Language 2057 ) KEY INDEX PK__Products__3213E83F67020225 ON my_catalog --Unique index WITH CHANGE_TRACKING AUTO --Population type; GO insert into Products values('Mouse Anti-Cattle FOXP3','This is Mouse Anti-Cattle FOXP3 introduction'); insert into Products values('Rabbit Anti-Mouse KRT13','This is Rabbit Anti-Mouse KRT13 introduction'); insert into Products values('Mouse Anti-C elegans FOXP3','This is Mouse Anti-C elegans FOXP3 introduction');
案例查询SQL语句:
declare @terms nvarchar(255) = 'Mouse Anti'; declare @termsQuote nvarchar(255) = '"'+@terms+'"'; select * from Products p JOIN ( SELECT tp.[id], (case when name like @terms+'%' then 4 when name like '%'+@terms then 3 when contains(name,@termsQuote) then 2 else 1 end) as 'sort' FROM [Products] tp where FREETEXT(description,@termsQuote) or Contains(name,@termsQuote) ) tp ON p.id = tp.id order by tp.sort desc;
上面的查询比使用传统的LIKE查询功能要加强很多。其实Contains和Freetext还有许多其它的用法,上面只是展现了一下基本用法,。上面的查询语句仅仅是作了关键词匹配,在完成接下来的章节学习后,将增长以下功能:
上面的四点问题中,在讨论完下一节《4. 如何查看本身的数据都被分解成了那些关键字》后,笔者会解决上面的局限性问题,以及提供一些合理的建议。
当咱们创建全文索引时,首先须要给全文索引指定一个语言编号,而后全文索引会根据不一样的语言编号使用不一样的语言分割器,不一样的分割器会分割出不一样的关键词列表。当使用全文索引查询数据时,就会匹配分割获得的关键词列表。
查看全部注册了的词语分割器列表
EXEC sp_help_fulltext_system_components 'wordbreaker';
查看全部支持的语言列表
select * from sys.fulltext_languages
使用dm_fts_parser分解语句得到关键词列表
-- 分解语句:She catches a cat SELECT * FROM sys.dm_fts_parser ( '"She catches a cat"', --待分解的语句 1033, --语言编号, 1033表明English,语言编号信息能够经过查看sys.fulltext_languages表获取 0, -- stoplist: 0表示使用默认的,NULL表示不使用。 0 -- accent_sensitivity,0表示insensitivity,1表示sensitivity ); --查看一个表被分解成的全部关键词列表 SELECT * FROM sys.dm_fts_index_keywords_by_document( db_id('Test'), -- 数据库对象ID object_id('dbo.Text_Test_Table') -- 表对象ID )
到这里,咱们已经知道如何分解语句获取关键词列表。接下来咱们将继续优化上面的查询案例,第一点是如何结合SOUNDEX和全文索引创建关键词搜索纠错机制,第二点是如何创建一个搜索提示功能。
当用户搜索某关键词语句时,若是有其它的关键词和用户搜索的关键词发音是同样的,那么就须要提示给用户。就相似Google搜索这样的提供功能。
当在Google搜索 she catsh a cat的时候,Google会提示是不是指 she catch a cat. 相似这样的纠错提示,咱们能够经过下面这个T-SQL实现一个简单的版本。
declare @searchTerms nvarchar(1000) = N'"she catsh a cat"'; select * from ( select display_term,SOUNDEX(display_term) as st_soundindex from sys.dm_fts_parser( @searchTerms, (select lcid from sys.fulltext_languages where [name] = 'British English'),--语言须要和Products表使用的语言保持一致 NULL, 0 )) st join (SELECT display_term,SOUNDEX(display_term) o_soundindex FROM sys.dm_fts_index_keywords_by_document(db_id('Test'), object_id('dbo.Products'))) ot on st.display_term != ot.display_term and st.st_soundindex = ot.o_soundindex;
效果图:
经过上面的脚本对比,咱们就能够查询出catsh和catch是同音的,而后程序就能够拿到这个结果给用户作纠错提示了。
关于搜索提示这部分,很是遗憾,全文索引并未提供相关的功能。这部分须要程序开发本身来实现,这里笔者谈一谈本身的实现思路:
到这里的,全文索引的主体知识点本身都讲到了。全文索引就是一个特定语言的搜索功能(Language-Specific Search),因此给本身的数据指定语言类型是很是重要的。全文索引的功能较LIKE的功能有所提高,但全文索引也不是万能的(咱们也不能指望全文索引把全部功能都完成),好比: