MySQL模糊查询不能用 like,头真大

本文介绍了InnoDB在MySQL中的全文检索功能,包括倒排索引的原理、如何在表中创建全文索引、以及NaturalLanguage、布尔搜索和查询扩展的不同查询模式。还讨论了如何处理停用词和优化查询效率。

前言

我们都知道 InnoDB 在模糊查询数据时使用 "%xx" 会导致索引失效,但有时需求就是如此,类似这样的需求还有很多,例如,搜索引擎需要根基用户数据的关键字进行全文查找,电子商务网站需要根据用户的查询条件,在可能需要在商品的详细介绍中进行查找,这些都不是B+树索引能很好完成的工作。

通过数值比较,范围过滤等就可以完成绝大多数我们需要的查询了。但是,如果希望通过关键字的匹配来进行查询过滤,那么就需要基于相似度的查询,而不是原来的精确数值比较,全文索引就是为这种场景设计的。

全文索引(Full-Text Search)是将存储于数据库中的整本书或整篇文章中的任意信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词等信息,也可以进行各种统计和分析。

在早期的 MySQL 中,InnoDB 并不支持全文检索技术,从 MySQL 5.6 开始,InnoDB 开始支持全文检索。

倒排索引

全文检索通常使用倒排索引(inverted index)来实现,倒排索引同 B+Tree 一样,也是一种索引结构。它在辅助表中存储了单词与单词自身在一个或多个文档中所在位置之间的映射,这通常利用关联数组实现,拥有两种表现形式:

  • inverted file index:{单词,单词所在文档的id}

  • full inverted index:{单词,(单词所在文档的id,再具体文档中的位置)}

上图为 inverted file index 关联数组,可以看到其中单词"code"存在于文档1,4中,这样存储再进行全文查询就简单了,可以直接根据 Documents 得到包含查询关键字的文档;而 full inverted index 存储的是对,即(DocumentId,Position),因此其存储的倒排索引如下图,如关键字"code"存在于文档1的第6个单词和文档4的第8个单词。

相比之下,full inverted index 占用了更多的空间,但是能更好的定位数据,并扩充一些其他搜索特性。

全文检索

创建全文索引
1、创建表时创建全文索引语法如下:
CREATE TABLE table_name ( id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, author VARCHAR(200), 
title VARCHAR(200), content TEXT(500), FULLTEXT full_index_name (col_name) ) ENGINE=InnoDB;

输入查询语句:

SELECT table_id, name, space from INFORMATION_SCHEMA.INNODB_TABLES
WHERE name LIKE 'test/%';

 

上述六个索引表构成倒排索引,称为辅助索引表。当传入的文档被标记化时,单个词与位置信息和关联的DOC_ID,根据单词的第一个字符的字符集排序权重,在六个索引表中对单词进行完全排序和分区。

2、在已创建的表上创建全文索引语法如下:
CREATE FULLTEXT INDEX full_index_name ON table_name(col_name);
使用全文索引

MySQL 数据库支持全文检索的查询,全文索引只能在 InnoDB 或 MyISAM 的表上使用,并且只能用于创建 char,varchar,text 类型的列。

其语法如下:

MATCH(col1,col2,...) AGAINST(expr[search_modifier])
search_modifier:
{
    IN NATURAL LANGUAGE MODE
    | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
    | IN BOOLEAN MODE
    | WITH QUERY EXPANSION
}

全文搜索使用 MATCH() AGAINST()语法进行,其中,MATCH()采用逗号分隔的列表,命名要搜索的列。AGAINST()接收一个要搜索的字符串,以及一个要执行的搜索类型的可选修饰符。全文检索分为三种类型:自然语言搜索、布尔搜索、查询扩展搜索,下面将对各种查询模式进行介绍。

Natural Language

自然语言搜索将搜索字符串解释为自然人类语言中的短语,MATCH()默认采用 Natural Language 模式,其表示查询带有指定关键字的文档。

接下来结合demo来更好的理解Natural Language

SELECT
    count(*) AS count 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( 'MySQL' );

                                                                           

上述语句,查询 title,body 列中包含 'MySQL' 关键字的行数量。上述语句还可以这样写:

SELECT
    count(IF(MATCH ( title, body ) 
    against ( 'MySQL' ), 1, NULL )) AS count 
FROM
    `fts_articles`;

上述两种语句虽然得到的结果是一样的,但从内部运行来看,第二句SQL的执行速度更快些,因为第一句SQL(基于where索引查询的方式)还需要进行相关性的排序统计,而第二种方式是不需要的。

还可以通过SQL语句查询相关性:

SELECT
    *,
    MATCH ( title, body ) against ( 'MySQL' ) AS Relevance 
FROM
    fts_articles;

相关性的计算依据以下四个条件:

  • word 是否在文档中出现

  • word 在文档中出现的次数

  • word 在索引列中的数量

  • 多少个文档包含该 word

对于 InnoDB 存储引擎的全文检索,还需要考虑以下的因素:

  • 查询的 word 在 stopword 列中,忽略该字符串的查询

  • 查询的 word 的字符长度是否在区间 [innodb_ft_min_token_size,innodb_ft_max_token_size] 内

如果词在 stopword 中,则不对该词进行查询,如对 'for' 这个词进行查询,结果如下所示:

SELECT
    *,
    MATCH ( title, body ) against ( 'for' ) AS Relevance 
FROM
    fts_articles;

 

可以看到,'for'虽然在文档 2,4中出现,但由于其是 stopword ,故其相关性为0

参数 innodb_ft_min_token_size 和 innodb_ft_max_token_size 控制 InnoDB 引擎查询字符的长度,当长度小于 innodb_ft_min_token_size 或者长度大于 innodb_ft_max_token_size 时,会忽略该词的搜索。在 InnoDB 引擎中,参数 innodb_ft_min_token_size 的默认值是3,innodb_ft_max_token_size的默认值是84

Boolean

布尔搜索使用特殊查询语言的规则来解释搜索字符串,该字符串包含要搜索的词,它还可以包含指定要求的运算符,例如匹配行中必须存在或不存在某个词,或者它的权重应高于或低于通常情况。

例如,下面的语句要求查询有字符串"Pease"但没有"hot"的文档,其中+和-分别表示单词必须存在,或者一定不存在。

select * from fts_test where MATCH(content) AGAINST('+Pease -hot' IN BOOLEAN MODE);

Boolean 全文检索支持的类型包括:

  • +:表示该 word 必须存在

  • -:表示该 word 必须不存在

  • (no operator)表示该 word 是可选的,但是如果出现,其相关性会更高

  • @distance表示查询的多个单词之间的距离是否在 distance 之内,distance 的单位是字节,这种全文检索的查询也称为 Proximity Search,如 MATCH(context) AGAINST('"Pease hot"@30' IN BOOLEAN MODE)语句表示字符串 Pease 和 hot 之间的距离需在30字节内

  • >:表示出现该单词时增加相关性

  • <:表示出现该单词时降低相关性

  • ~:表示允许出现该单词,但出现时相关性为负

  • * :表示以该单词开头的单词,如 lik*,表示可以是 lik,like,likes

  • " :表示短语

下面是一些demo,看看 Boolean Mode 是如何使用的。

demo1:+ -

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( '+MySQL -YourSQL' IN BOOLEAN MODE );

上述语句,查询的是包含 'MySQL' 但不包含 'YourSQL' 的信息

demo2:no operator

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( 'MySQL IBM' IN BOOLEAN MODE );

上述语句,查询的 'MySQL IBM' 没有 '+','-'的标识,代表 word 是可选的,如果出现,其相关性会更高

demo3:@

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( '"DB2 IBM"@3' IN BOOLEAN MODE );

上述语句,代表 "DB2" ,"IBM"两个词之间的距离在3字节之内

demo4:> <

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( '+MySQL +(>database <DBMS)' IN BOOLEAN MODE );

上述语句,查询同时包含 'MySQL','database','DBMS' 的行信息,但不包含'DBMS'的行的相关性高于包含'DBMS'的行。

demo5: ~

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( 'MySQL ~database' IN BOOLEAN MODE );

上述语句,查询包含 'MySQL' 的行,但如果该行同时包含 'database',则降低相关性。

demo6:*

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( 'My*' IN BOOLEAN MODE );

上述语句,查询关键字中包含'My'的行信息。

demo7:"

SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH ( title, body ) AGAINST ( '"MySQL Security"' IN BOOLEAN MODE );

上述语句,查询包含确切短语 'MySQL Security' 的行信息。

Query Expansion

查询扩展搜索是对自然语言搜索的修改,这种查询通常在查询的关键词太短,用户需要 implied knowledge(隐含知识)时进行,例如,对于单词 database 的查询,用户可能希望查询的不仅仅是包含 database 的文档,可能还指那些包含 MySQL、Oracle、RDBMS 的单词,而这时可以使用 Query Expansion 模式来开启全文检索的 implied knowledge通过在查询语句中添加 WITH QUERY EXPANSION / IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION 可以开启 blind query expansion(又称为 automatic relevance feedback),该查询分为两个阶段。

  • 第一阶段:根据搜索的单词进行全文索引查询

  • 第二阶段:根据第一阶段产生的分词再进行一次全文检索的查询

接着来看一个例子,看看 Query Expansion 是如何使用的。

-- 创建索引
create FULLTEXT INDEX title_body_index on fts_articles(title,body);
-- 使用 Natural Language 模式查询
SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH(title,body) AGAINST('database');

使用 Query Expansion 前查询结果如下:

-- 当使用 Query Expansion 模式查询
SELECT
    * 
FROM
    `fts_articles` 
WHERE
    MATCH(title,body) AGAINST('database' WITH QUERY expansion);

使用 Query Expansion 后查询结果如下:

由于 Query Expansion 的全文检索可能带来许多非相关性的查询,因此在使用时,用户可能需要非常谨慎。

删除全文索引
1、直接删除全文索引语法如下:
DROP INDEX full_idx_name ON db_name.table_name;
2、使用 alter table 删除全文索引语法如下:
ALTER TABLE db_name.table_name DROP INDEX full_idx_name;
<think>首先,用户的问题是:“我想查询MySQL中模糊删除时LIKE后面不能使用单引号的原因 请问MySQL模糊删除时LIKE后面不能用单引号的原因”。用户提到LIKE后面不能使用单引号,但我需要澄清这一点。在MySQL中,LIKE操作符用于模糊查询,通常与字符串模式和单引号一起使用。例如,引用[1]中显示:"SELECT ename,sal,comm,job FROM emp WHERE ename LIKE '%A%';",这里单引号是正确使用的。 用户在说“模糊删除时LIKE后面不能使用单引号”,但删除操作应该是DELETE语句。或许是混淆了。在MySQL中,模糊删除通常是通过DELETE语句结合WHERE子句中使用LIKE来实现的。例如:"DELETE FROM emp WHERE ename LIKE '%A%';"。在这种语句中,LIKE后面应该使用单引号来包围模式字符串。 用户在问“不能使用单引号的原因”,但实际上是可以使用的。可能用户遇到了错误或误解。或许是用户在尝试时遇到了问题,比如语法错误。 回顾系统指令:我需要使用正确的LaTeX格式,结构清晰,使用中文回答,并生成相关问题。引用用户提供的引用材料。 用户提供了几个引用: - 引用[1]:关于LIKE运算符用于模糊查询的例子,使用单引号。 - 引用[2]:关于MySQL密码重置。 - 引用[3]:关于创建表的基本知识。 - 引用[4]:关于左外连接等。 这些引用中,只有引用[1]直接相关,因为它涉及LIKE操作符和单引号的使用。 关键点:在SQL语法中,字符串必须用引号包围,在MySQL中通常是单引号或双引号。LIKE操作符后面需要一个字符串模式,所以必须用引号。例如,正确的写法是:WHERE column LIKE 'pattern'。 如果用户说“不能使用单引号”,可能是因为: 1. 用户忘记了引号或使用了错误的引号类型。 2. 在某些上下文中,模式包含单引号本身,导致转义问题。 3. 用户误解了语法。 在模糊删除中,DELETE语句类似于SELECT,但用于删除行。LIKE的使用相同。 可能的错误场景: - 如果模式中包含单引号,需要用转义字符。例如,WHERE ename LIKE 'O\'Brien%'。 - 或者在模式中使用了双引号,但MySQL默认支持单引号。 在MySQL中,字符串可以用单引号或双引号包围,但单引号更常见和标准。LIKE后面必须使用引号来定义字符串。 所以,用户的问题可能源于一个具体的错误。用户在问“原因”,我需要解释为什么LIKE后面需要引号,或者为什么不能省略引号。 重构问题:MySQL模糊删除(DELETE with LIKE)时,LIKE后面必须使用引号(单引号或双引号),而是“不能用单引号”。用户可能想说“为什么有时出错”,但具体说是“不能用单引号”。 从引用[1]:"SELECT ename,sal,comm,job FROM emp WHERE ename LIKE '%A%';" – 这里用了单引号,是正确的。 在模糊删除中: - 正确的语法:"DELETE FROM emp WHERE ename LIKE '%A%';" - 如果省略引号,例如:WHERE ename LIKE %A%,这会导致语法错误,因为%A%是一个有效的标识符或值。 所以,原因LIKE后面必须使用引号的原因是:LIKE操作符需要一个字符串模式作为操作数,而字符串在SQL中必须用引号包围以区分它们从列名或其他标识符。 如果使用引号,MySQL会解析模式作为列名或关键字,导致错误。 例如: - WHERE ename LIKE A% – 如果A是一个列名,这可能会被解释为ename LIKE A%,但A%是有效,除非A是列。 - 更准确地说,缺少引号会使模式被视为表达式或列名,而是字符串。 因此,LIKE后面必须使用引号来指定字符串模式。 在用户的问题中,说“不能使用单引号”,但实际上是必须使用。可能用户在尝试时遇到了转义问题或其他错误。 基于引用材料:引用[1]明确展示了LIKE with single quotes。 现在,构建回答: 1. 结构化清晰:逐步解释。 2. 使用中文。 3. 引用相关段落:引用[1]中提到了LIKE使用单引号。 4. 生成相关问题。 5. 确保真实可靠。 回答大纲: - 澄清误解:在MySQL模糊查询或删除中,LIKE后面必须使用单引号(或双引号)来包围模式字符串。 - 解释原因:因为SQL语法要求字符串文字用引号包围,以区分于标识符。 - 给出正确语法示例。 - 如果用户提到不能使用,可能是由于转义或特定字符问题。 - 引用材料:参考引用[1] "SELECT ... WHERE ename LIKE '%A%';" 这表明了单引号的使用。 - 生成相关问题。 最终回答: - 开:直接回答问题。 - 主体:解释语法规则。 - 结束:生成相关问题。 相关问题想法: 1. 如何在MySQL中使用LIKE操作符进行模糊删除? 2. 如何处理在LIKE模式中包含单引号的情况? 3. MySQL中字符串引号的规则是什么? 4. 模糊删除与模糊查询的语法区别? </think>根据您的查询,我将解释MySQL中模糊删除(即使用`DELETE`语句结合`LIKE`操作符进行模式匹配删除)时`LIKE`后面不能缺失单引号的原因,并参考提供的站内引用材料。核心问题是:**在模糊删除中,`LIKE`后面必须使用单引号(或双引号)来包围模式字符串,而不能省略引号**。如果省略引号,会导致语法错误。以下我将逐步分析原因,并提供清晰的解释。 ### 原因分析 1. **SQL语法要求字符串必须使用引号包围**: - 在MySQL中,`LIKE`操作符用于模糊匹配模式,它需要一个字符串作为操作数(例如 `'%A%'` 表示包含"A"的任意字符串)。 - SQL标准规定,字符串文字(如模式字符串)必须用引号包围,以区别于列名、关键字或其他标识符。单引号(`' '`)是MySQL中最常用的方式,双引号(`" "`)在某些模式下也可以使用,但单引号更安全可靠。 - 如果省略引号,例如写成 `LIKE %A%`,MySQL会将 `%A%` 解释为一个表达式或列名,而非字符串模式,从而引发语法错误。例如: - 正确语法:`DELETE FROM emp WHERE ename LIKE '%A%';` - 错误语法:`DELETE FROM emp WHERE ename LIKE %A%;` (这会报错,因为 `%A%` 未被识别为字符串)。 2. **模糊删除与模糊查询的语法一致性**: - 模糊删除(`DELETE`)和模糊查询(`SELECT`)在`WHERE`子句中使用`LIKE`的规则相同。引用[1]中展示了`SELECT`语句的正确用法:`SELECT ename,sal,comm,job FROM emp WHERE ename LIKE '%A%';`,这里单引号是必需的以定义模式 `'%A%'`。 - 同样,在模糊删除中: - 正确写法:`DELETE FROM emp WHERE ename LIKE '_L%';` (删除名字第二个字符为"L"的记录)。 - 如果省略单引号,如 `LIKE _L%`,MySQL会将 `_L%` 视为无效标识符,导致错误如 `You have an error in your SQL syntax`。 3. **为什么用户可能误以为“不能使用单引号”**: - **转义问题**:如果模式字符串本身包含单引号(例如删除名字为 `O'Brien` 的记录),需要使用转义字符。例如:`LIKE 'O\'Brien%'`。若未正确转义,MySQL会将单引号视为字符串结束符,导致语法错误。这可能让用户误以为“单引号不能用”,但实际上是需要正确处理。 - **配置或模式差异**:在MySQL中,如果启用了`ANSI_QUOTES`模式,双引号可能被用于标识符(如列名),此时单引号更安全。但这是禁用单引号,而是推荐使用单引号作为字符串界定符。 - **具体错误示例**:假设尝试 `DELETE FROM emp WHERE ename LIKE %A%;`,错误信息可能为:`Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version...`。这源于语法规则,而非单引号本身的问题。 ### 正确语法示例 基于引用[3],创建表后执行模糊删除(假设表名为 `emp`): ```sql -- 正确:使用单引号包围模式 DELETE FROM emp WHERE ename LIKE '%A%'; -- 删除名字包含"A"的记录 DELETE FROM emp WHERE ename LIKE '_L%'; -- 删除名字第二个字符为"L"的记录 -- 错误:省略单引号(会报语法错误) -- DELETE FROM emp WHERE ename LIKE %A%; ``` - 注意:模式中的 `%` 表示零或多个字符, `_` 表示一个字符,如引用[1]所述。单引号确保这些通配符被正确解析为字符串的一部分[^1]。 ### 总结 **`LIKE`后面必须使用单引号的原因**:SQL语法强制要求字符串模式用引号包围,以明确区分字符串与数据库对象(如列名)。省略引号会导致MySQL解析失败,引发语法错误。这MySQL的bug,而是SQL标准的设计。同时,确保转义特殊字符(如单引号自身)可避免常见错误。更多基础语法可参考引用[3]中的表操作示例[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值