mysql 关联条件 中使用 like

本文介绍了一种使用SQL进行复杂条件筛选的方法,通过连接两个表并利用LIKE语句匹配关键词,实现了从大量数据中精确查找含有特定关键词组合的数据记录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

select 
*
from 
(
select a.*,
			concat(b.id,'-',b.first_key,'-',b.second_key) as contain_key_word
 from key_data_0503 a
  join key_word b
on a.question_desc like  concat('%' ,b.first_key ,'%')
and a.question_desc like concat('%' ,b.second_key ,'%')
#where a.create_time < '2017-04-17'
order by a.create_time
) t
group by id


<think>我们正在讨论如何优化和替代MySQL中的NOT LIKE操作。根据引用[2]和引用[4],我们可以使用INSTR函数来替代LIKE操作,同样,NOT LIKE也可以使用INSTR函数来替代。 引用[2]中提到: SELECT name FROM employees where INSTR(name,'王')=0 //相当于 not like '%王%' 因此,我们可以使用INSTR(column, str) = 0 来替代 column NOT LIKE '%str%' 另外,引用[3]提到,使用OR可能会造成全表扫描,因此我们应尽量避免在索引列上使用OR,可以考虑使用UNION来优化。 但是,对于NOT LIKE操作,由于它通常会导致全表扫描(因为无法使用索引),我们需要考虑其他优化方法。 优化技巧: 1. 使用全文本索引(FULLTEXT INDEX): 如果我们需要进行全文搜索,可以考虑使用MySQL的全文本索引。但是注意,全文本索引不支持NOT操作,但我们可以用其他方式模拟。 例如:MATCH(column) AGAINST('keyword' IN BOOLEAN MODE) 然后排除包含关键字的行。 2. 使用覆盖索引(Covering Index): 如果查询的列都包含在索引中,那么即使使用NOT LIKE,也可以从索引中获取数据而不需要回表,这样会快一些。 3. 避免在索引列上使用NOT LIKE: 如果列上有索引,使用NOT LIKE '%...'(前导通配符)会导致索引失效,因为MySQL无法利用B树索引进行前缀匹配。但是,如果没有前导通配符,比如NOT LIKE 'abc%',那么索引可能被使用(因为相当于范围查询)。 4. 使用反向逻辑: 有时候,我们可以使用反向逻辑来避免NOT LIKE。例如,如果我们想要所有不包含“abc”的记录,我们可以先找出包含“abc”的记录,然后使用NOT IN(但要注意NULL值)。但这种方法在数据量大的时候可能效率也不高。 5. 使用正则表达式: 使用REGEXP_LIKEMySQL 8.0+)或者RLIKE,但是正则表达式通常也不会使用索引,所以性能可能和NOT LIKE差不多。 6. 使用应用程序处理: 如果数据量不大,可以在应用程序中过滤,避免在数据库中进行复杂的操作。 7. 考虑使用其他存储引擎: 比如使用Elasticsearch等全文搜索引擎来处理复杂的文本搜索,包括否定搜索。 8. 使用物化视图或中间表: 如果查询频繁且数据变化不频繁,可以创建中间表存储结果。 9. 使用EXISTS或LEFT JOIN: 在某些情况下,我们可以用LEFT JOIN和IS NULL来模拟NOT LIKE,但这种方法通常用于关联查询,而不是单个表的条件。 10. 数据归档: 如果数据量非常大,考虑将不经常访问的数据归档,减少查询的数据量。 由于NOT LIKE操作本质上难以优化,特别是在使用前导通配符时,因此我们应尽量考虑替代方案。 替代方案示例: 使用INSTR函数: 原始查询:SELECT * FROM table WHERE column NOT LIKE '%abc%'; 替代为:SELECT * FROM table WHERE INSTR(column, 'abc') = 0; 但是,INSTR函数同样无法使用索引(除非使用函数索引,MySQL 8.0支持函数索引)。 在MySQL 8.0中,我们可以创建函数索引来优化INSTR函数: CREATE INDEX idx_column_instr ON table ((INSTR(column, 'abc'))); 然后查询:SELECT * FROM table WHERE INSTR(column, 'abc') = 0; 但是,这种方法需要提前知道要查询的字符串,而实际查询中字符串可能是变化的,所以不太实用。 因此,更通用的优化方法可能是使用全文本索引或者改变数据模型。 全文本索引示例(仅适用于MyISAM和InnoDB,且要求字段为CHAR、VARCHAR或TEXT): 创建索引:ALTER TABLE table ADD FULLTEXT INDEX ft_index (column); 查询:SELECT * FROM table WHERE MATCH(column) AGAINST('abc' IN BOOLEAN MODE); 但是,全文本索引默认是自然语言搜索,它不支持直接否定一个词(即排除包含某个词的记录)。在布尔模式下,我们可以使用减号: SELECT * FROM table WHERE MATCH(column) AGAINST('-abc' IN BOOLEAN MODE); 注意:这种方法只返回不包含'abc'的记录,但要求全文本索引的最小词长度(ft_min_word_len)要小于等于'abc'的长度(通常默认是4,所以'abc'可能不会被索引)。因此,需要调整配置或者使用更长的词。 总结: - 如果可能,尽量避免使用NOT LIKE,特别是在大数据量且要求高性能的场景。 - 考虑使用全文本索引(布尔模式)来替代,但要注意配置和词语长度。 - 在MySQL 8.0中,可以考虑使用函数索引来优化INSTR函数。 - 如果查询条件中的字符串是固定的,可以考虑使用生成列(Generated Column)并对其建立索引。 生成列示例(MySQL 5.7+): ALTER TABLE table ADD COLUMN column_abc_flag TINYINT GENERATED ALWAYS AS (INSTR(column, 'abc') > 0) STORED, ADD INDEX idx_abc_flag (column_abc_flag); 然后查询:SELECT * FROM table WHERE column_abc_flag = 0; 这种方法将计算提前存储在列中,并建立索引,查询时可以直接使用索引。但是,这需要额外的存储空间,并且在插入和更新时有额外的计算开销。 根据实际需求选择合适的方法。</think>### MySQL NOT LIKE替代方案与优化技巧 在MySQL中,`NOT LIKE`操作可能导致全表扫描和性能问题,尤其在大数据量场景下。以下是高效替代方案和优化技巧: --- ### 一、直接替代方案 1. **`INSTR()`函数** 利用字符串位置判断替代`NOT LIKE`,语义更清晰: ```sql -- 等效于 NOT LIKE '%keyword%' SELECT * FROM table WHERE INSTR(column, 'keyword') = 0; -- 等效于 NOT LIKE 'keyword%' SELECT * FROM table WHERE INSTR(column, 'keyword') <> 1; ``` *优势*:避免通配符解析开销,部分场景可提升性能[^2][^4]。 2. **`LOCATE()`函数** 与`INSTR()`功能相同但参数顺序相反: ```sql SELECT * FROM table WHERE LOCATE('keyword', column) = 0; ``` --- ### 二、深度优化技巧 1. **反向逻辑 + 覆盖索引** 通过正向查询排除结果,结合覆盖索引减少I/O: ```sql -- 假设id为主键 SELECT * FROM table WHERE id NOT IN ( SELECT id FROM table WHERE column LIKE '%keyword%' ); ``` *适用场景*:子查询结果集较小且`column`有索引时[^3]。 2. **生成列 + 索引(MySQL 5.7+)** 创建存储计算结果的生成列并建立索引: ```sql ALTER TABLE table ADD COLUMN keyword_flag TINYINT GENERATED ALWAYS AS (INSTR(column, 'keyword')) STORED, ADD INDEX (keyword_flag); SELECT * FROM table WHERE keyword_flag = 0; ``` *优势*:查询时直接使用索引,但增加存储开销[^4]。 3. **全文索引(FULLTEXT)** 对文本列创建全文索引实现高效否定搜索: ```sql ALTER TABLE table ADD FULLTEXT(column); -- 布尔模式排除特定词 SELECT * FROM table WHERE MATCH(column) AGAINST(' -keyword' IN BOOLEAN MODE); ``` *限制*:仅适用于MyISAM/InnoDB,且需调整最小词长配置[^1]。 --- ### 三、性能规避策略 1. **避免前导通配符** `NOT LIKE '%abc'` 必然全表扫描,优先考虑: - 使用后缀索引(如反转字符串存储) - 改用`RIGHT(column,3) <> 'abc'` 2. **分区过滤** 对表按时间或范围分区,缩小扫描范围: ```sql SELECT * FROM partitioned_table WHERE partition_column BETWEEN '2023-01-01' AND '2023-12-31' AND column NOT LIKE '%keyword%'; ``` 3. **异步处理** 对实时性要求低的场景: - 用定时任务将`NOT LIKE`结果写入缓存表 - 查询直接访问结果表 --- ### 四、方案选择建议 | **场景** | **推荐方案** | |---------------------------|----------------------------| | 短文本且无索引 | `INSTR()`或`LOCATE()` | | 高频查询的固定条件 | 生成列+索引 | | 海量文本搜索 | 全文索引(布尔模式) | | 只读历史数据分析 | 异步结果表 | > **关键原则**:所有方案需配合`EXPLAIN`验证执行计划,优先保证索引有效利用。对于超大数据集,建议结合Elasticsearch等专业搜索引擎[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值