0

0

sql语句怎样避免因like查询使用通配符开头导致的索引失效 sql语句like通配符开头致索引失效的常见问题解决

星夢妙者

星夢妙者

发布时间:2025-08-20 15:35:01

|

902人浏览过

|

来源于php中文网

原创

使用LIKE '%通配符'会因B-tree索引无法支持后缀匹配而导致全表扫描,解决方法包括:1. 采用全文检索(如MySQL FULLTEXT、PostgreSQL GIN索引)高效处理任意子串查询;2. 对后缀查询使用逆序存储并创建索引;3. 优化业务逻辑,优先前缀匹配或精确查询;4. 在数据量小或低频场景下可容忍全表扫描;5. 避免在索引列使用函数、隐式类型转换、OR条件等导致索引失效的操作;选择方案需结合查询模式、数据规模、数据库能力及维护成本,并通过EXPLAIN验证执行计划。

sql语句怎样避免因like查询使用通配符开头导致的索引失效 sql语句like通配符开头致索引失效的常见问题解决

SQL语句中使用

LIKE '%通配符'
开头进行查询,确实是个性能杀手,因为它几乎总是导致索引失效,进而引发全表扫描。要避免这个问题,核心思路就是想办法让查询条件能够利用到索引,或者干脆绕开传统索引的限制。

解决方案

解决

LIKE '%通配符'
导致索引失效的问题,主要有以下几种策略,每种都有其适用场景和考量:

1. 利用全文检索(Full-Text Search)

这是处理模糊查询,特别是包含任意位置子串查询的最优解之一。主流数据库如MySQL、PostgreSQL、SQL Server都有内置的全文检索功能,或者可以集成Elasticsearch、Solr等外部搜索引擎。

  • MySQL的全文索引: 适用于

    MyISAM
    InnoDB
    存储引擎(MySQL 5.6+)。你需要为要查询的列创建
    FULLTEXT
    索引。

    ALTER TABLE your_table ADD FULLTEXT(your_column);
    SELECT * FROM your_table WHERE MATCH(your_column) AGAINST('keyword' IN NATURAL LANGUAGE MODE);

    它的工作原理是为文本内容建立倒排索引,能高效地查找包含特定词语的文档。对于中文,可能需要额外的分词插件。

  • PostgreSQL的

    tsvector
    tsquery
    PostgreSQL的全文检索功能非常强大和灵活。

    -- 创建索引
    CREATE INDEX idx_your_column_gin ON your_table USING GIN(to_tsvector('english', your_column));
    -- 查询
    SELECT * FROM your_table WHERE to_tsvector('english', your_column) @@ to_tsquery('english', 'keyword');

    它允许你定义不同的语言配置和词典,处理多语言文本能力出色。

2. 逆序存储与查询(Reverse Indexing)

这是一种比较巧妙的技巧,但有其局限性。如果你的查询模式主要是

LIKE '%keyword'
(即以某个词结尾),你可以考虑在表中增加一个额外的列,存储原始列内容的逆序字符串,并在这个逆序列上创建索引。

例如,如果原列是

'apple'
,逆序列就存
'elppa'
。当你想查找
LIKE '%ple'
时,实际上查询逆序列的
LIKE 'elp%'
,这样就能利用到索引了。

-- 增加逆序列
ALTER TABLE your_table ADD COLUMN your_column_reversed VARCHAR(255);
-- 插入或更新时同步逆序数据
UPDATE your_table SET your_column_reversed = REVERSE(your_column);
-- 为逆序列创建索引
CREATE INDEX idx_your_column_reversed ON your_table (your_column_reversed);
-- 查询时
SELECT * FROM your_table WHERE your_column_reversed LIKE REVERSE('keyword%'); -- 注意这里的keyword%

这种方法适用于你明确知道需要进行后缀匹配的场景,但对于任意位置的子串匹配(

%keyword%
),它就无能为力了。而且,它增加了存储开销和数据同步的复杂性。

3. 优化业务逻辑,避免前置通配符

这听起来有点像“废话”,但很多时候,我们确实可以从业务层面重新审视需求。用户真的需要查找任意位置的子串吗?

  • 能否只允许前缀匹配? 如果业务允许,强制用户输入前缀,例如搜索“苹果”相关的商品,用户输入“苹果”,而不是“果”。这样就可以用
    LIKE 'keyword%'
    ,这就能很好地利用B-tree索引。
  • 拆分搜索词? 如果用户输入“red apple”,能否将“red”和“apple”拆开,分别作为前缀进行多次查询,或者结合
    AND
    条件?
  • 精确匹配优先? 很多搜索场景下,用户可能更倾向于精确匹配,模糊查询只是辅助。可以先尝试精确匹配,如果无结果再进行模糊匹配。

4. 牺牲性能,但确保查询能跑

在数据量不大,或者查询频率极低的情况下,直接使用

LIKE '%keyword%'
可能不是最坏的选择。但前提是你真的评估过,它的性能影响在可接受范围内。

  • 使用

    INSTR()
    LOCATE()
    函数:
    这些函数可以查找子串的位置,但它们本质上也是全表扫描。

    SELECT * FROM your_table WHERE INSTR(your_column, 'keyword') > 0;

    相比

    LIKE '%keyword%'
    ,在某些数据库和特定场景下,它们的执行计划可能会略有不同,但通常不会带来质的提升。

  • 考虑数据结构调整: 如果你的业务场景就是频繁进行这类模糊查询,并且数据量巨大,那么可能需要重新思考数据存储结构,比如将需要模糊查询的文本内容独立出来,存储到专门的搜索引擎中。

    阿里妈妈·创意中心
    阿里妈妈·创意中心

    阿里妈妈营销创意中心

    下载

为什么
LIKE '%通配符'
会导致索引失效?

这个问题,说白了,就是B-tree索引的特性决定的。B-tree索引是一种平衡树结构,它能高效地处理等值查询(

=
)和范围查询(
>
<
BETWEEN
),以及前缀匹配的
LIKE
查询(
LIKE 'keyword%'
)。

当你创建了一个B-tree索引,比如在

product_name
列上,数据库会把
product_name
的所有值按字母顺序排列好,并存储在索引中。

  • LIKE 'apple%'
    数据库可以很轻松地在索引中找到以“apple”开头的第一条记录,然后顺着索引的叶子节点往后扫描,直到遇到不以“apple”开头的记录为止。这个过程是利用索引的有序性进行的,非常高效。

  • LIKE '%apple'
    问题来了。数据库不知道以“apple”结尾的字符串在索引的哪个位置开始。它无法利用索引的排序特性来快速定位。想象一下,索引里有“banana”、“orange”、“pineapple”,你要找以“apple”结尾的,它得从头到尾把所有索引项(或者更糟糕,直接是表数据)都看一遍,看看哪个字符串的末尾是“apple”。这本质上就是全表扫描。

  • LIKE '%apple%'
    同理,如果通配符在两边,那更没办法利用索引了。数据库无法判断“apple”这个子串会出现在哪个位置,只能遍历所有数据。

所以,核心原因就是B-tree索引的有序性无法满足后缀或中缀匹配的需求,导致查询优化器不得不放弃索引,转而进行代价更高的全表扫描。

除了全表扫描,还有哪些潜在的性能陷阱?

SQL查询性能的坑远不止

LIKE '%通配符'
这一种,很多时候,一些看似无害的操作,都可能让你的查询效率直线下降。在我看来,以下几点是特别常见的“陷阱”:

  1. 在索引列上使用函数或进行表达式操作: 当你在

    WHERE
    子句中对索引列应用函数(如
    DATE()
    ,
    YEAR()
    ,
    SUBSTRING()
    ,
    UPPER()
    ,
    LOWER()
    等)或者进行算术运算时,数据库同样无法直接利用索引。

    -- 索引失效:对索引列使用了函数
    SELECT * FROM orders WHERE YEAR(order_date) = 2023;
    -- 优化方式:将函数作用于常量,而不是列
    SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31';

    数据库需要先计算出每一行的函数结果,然后才能进行比较,这使得它无法在索引树中直接查找。

  2. 隐式类型转换 如果你的查询条件中的数据类型与列的数据类型不匹配,数据库可能会尝试进行隐式类型转换。这个转换过程可能导致索引失效。

    -- 假设user_id是INT类型,但你用字符串比较
    SELECT * FROM users WHERE user_id = '123';

    数据库可能会把

    user_id
    列的每个值都转换为字符串再进行比较,或者把
    '123'
    转换为数字,但如果转换发生在列上,就可能导致问题。

  3. OR
    条件:
    WHERE
    子句中包含多个
    OR
    条件,并且这些条件涉及不同的列时,优化器可能难以有效地使用索引,甚至可能导致全表扫描。

    SELECT * FROM products WHERE product_name LIKE 'A%' OR category_id = 10;

    虽然某些数据库的优化器在特定情况下能处理好,但通常来说,如果两个条件都能使用各自的索引,优化器可能会选择合并索引扫描的结果,或者干脆进行全表扫描。有时拆分成

    UNION ALL
    可以改善性能,但也要具体分析。

  4. NOT IN
    <>
    (不等于)操作:
    NOT IN
    <>
    操作通常难以利用索引,因为它们表示的是“不包含”或“不等于”某个值,数据库需要扫描大量数据来确认哪些是不符合条件的。

    SELECT * FROM users WHERE status <> 'inactive';

    对于这种场景,如果“不等于”的值是少数,而“等于”的值是多数,可以考虑将查询条件反过来写,或者使用

    NOT EXISTS
    LEFT JOIN ... WHERE ... IS NULL

  5. 对NULL值的处理: 在某些数据库中,索引默认不包含NULL值。因此,涉及到

    IS NULL
    IS NOT NULL
    的查询可能无法有效利用索引。

    SELECT * FROM orders WHERE delivery_address IS NULL;

    这取决于索引类型和数据库配置。例如,MySQL的B-tree索引可以包含NULL值。但无论如何,查询NULL值通常意味着需要扫描索引中的所有NULL项,或者扫描表数据。

这些“陷阱”的共同点是,它们都可能阻止数据库有效地利用已有的索引结构,从而将查询的复杂度从对数级别(索引查找)提升到线性级别(全表扫描)。

如何选择适合的优化方案?

选择一个合适的SQL优化方案,绝不是拍脑袋决定的事,它需要我们像个侦探一样,深入挖掘问题的本质,并权衡各种利弊。没有银弹,只有最适合当前业务场景和数据特点的方案。

  1. 明确你的业务需求和查询模式: 这是最关键的一步。用户到底想怎么搜?是精确匹配?前缀匹配?后缀匹配?还是任意位置的模糊匹配?是搜索商品名还是文章内容?

    • 如果大部分是前缀匹配(
      LIKE 'keyword%'
      ),那么普通的B-tree索引就足够了。
    • 如果频繁需要任意位置的子串匹配(
      LIKE '%keyword%'
      ),且数据量大,全文检索几乎是唯一的正解。
    • 如果只是偶尔的后缀匹配,且数据量不大,逆序索引可能是一个轻量级的选择。 搞清楚这些,能帮你排除掉很多不必要的复杂方案。
  2. 分析数据量和增长趋势:

    • 数据量小(几千到几万): 很多时候,即使是全表扫描,性能也可能在可接受范围内。过度优化反而会增加开发和维护成本。
    • 数据量中等(几十万到几百万): 这时性能问题会逐渐显现,需要开始考虑索引优化。
    • 数据量大(千万上亿): 此时,任何导致全表扫描的查询都是灾难性的。必须使用高效的索引策略,如全文检索或分库分表。 同时,也要考虑数据是否持续增长,以及增长的速度。一个今天看起来没问题的方案,明天可能就扛不住了。
  3. 评估现有数据库的能力和资源: 你的数据库版本支持哪些高级特性?例如,MySQL 5.6+才支持

    InnoDB
    的全文索引,PostgreSQL的全文检索功能非常强大。

    • 数据库内置功能: 优先考虑利用数据库自带的优化功能,它们通常与数据库集成度最高,维护成本相对较低。
    • 外部搜索引擎: 如果数据库内置的全文检索功能无法满足需求(例如,需要更复杂的排名、高亮、多语言分词等),或者数据量非常庞大,那么集成Elasticsearch或Solr等外部搜索引擎是更好的选择。但这会增加系统架构的复杂性,需要额外的部署和维护成本。
  4. 权衡开发成本与维护成本:

    • 逆序存储: 虽然能解决特定问题,但它增加了额外的列,需要额外的逻辑来维护数据一致性(插入、更新时都需要同步逆序列),增加了开发和维护的复杂度。
    • 全文检索:
      • 数据库内置: 相对简单,配置和使用都比较直接。
      • 外部搜索引擎: 部署、配置、数据同步(ETL)、监控、故障排查等都需要投入资源。但它能提供更强大的搜索能力。 永远不要为了所谓的“极致性能”而过度设计,除非业务场景真的需要。
  5. 使用

    EXPLAIN
    (或等效工具)分析查询计划: 这是优化SQL的“诊断仪”。在任何优化之前和之后,都应该使用
    EXPLAIN
    来查看你的SQL语句是如何执行的。

    • 它会告诉你查询是否使用了索引,使用了哪个索引。
    • 它会显示扫描了多少行数据。
    • 它会揭示是否有全表扫描、临时表、文件排序等性能瓶颈。 通过
      EXPLAIN
      的输出,你可以验证你的优化方案是否真的起作用了,而不是凭空猜测。例如,看到
      type: ALL
      通常就意味着全表扫描,而
      type: ref
      type: range
      则表示使用了索引。

最终,选择优化方案是一个迭代的过程。先从最简单、最直接的方案开始,用

EXPLAIN
验证效果,如果不够再考虑更复杂的方案。始终以业务需求为导向,以实际性能提升为目标。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

706

2023.10.12

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

327

2023.10.27

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

348

2024.02.23

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

1201

2024.03.06

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

360

2024.03.06

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

778

2024.04.07

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

581

2024.04.29

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

421

2024.04.29

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

1

2026.01.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 9.5万人学习

CSS3 教程
CSS3 教程

共18课时 | 4.9万人学习

Django 教程
Django 教程

共28课时 | 3.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号