mysql in查询速度慢,试试改用join

本文探讨了一种SQL查询优化策略,通过将原本使用的IN子查询替换为LEFTJOIN,显著提升了查询速度。在项目中,对zt_proj_invoice_req表的查询由IN操作改为了LEFTJOIN,结合内部查询优化,减少了数据处理时间,从而提高了整体查询效率。这种方法对于大数据量的数据库操作尤其有益,是数据库性能调优的一个实例。

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

这是在一个项目中遇到的问题,项目原来的代码使用了in进行查询,速度非常慢,后面我使用left join之后速度得到很大的提升

修改前:

SELECT
	`c`.*,
	pir_total_amount,
CASE
		
		WHEN pci.count IS NULL THEN
		0 ELSE pci.count 
	END AS contract_count 
FROM
	zt_contract_info c
	LEFT JOIN ( SELECT contract_id, count( 1 ) AS count FROM zt_proj_contract_info zpci WHERE zpci.del = 0 GROUP BY contract_id ) pci ON `pci`.`contract_id` = `c`.`id`
	LEFT JOIN (
	SELECT
		pci.contract_id,
		sum( pir.total_amount ) AS pir_total_amount 
	FROM
		zt_proj_contract_info pci
		//这里使用了in进行查询
		LEFT JOIN zt_proj_invoice_req pir ON pir.del = 0 
		AND pir.parent_id IN (
		SELECT
			max( pf.id ) AS id 
		FROM
			zt_proj_form_info pf 
		WHERE
			pf.pid = pci.pid 
			AND form_id = 14 
			AND pf.del = 0 
			AND pf.stage = 99 
		) 
		//==============
	WHERE
		pci.del = 0 
	GROUP BY
		pci.contract_id 
	) sum_pir ON `sum_pir`.`contract_id` = `c`.`id` 
WHERE
	`c`.`del` = 0 
	AND ( c.del = 0 AND c.confirm_amount != sum_pir.pir_total_amount OR sum_pir.pir_total_amount IS NULL ) 
ORDER BY
	`c`.`id`

修改后:


	SELECT
		`c`.*,
		pir_total_amount,
	CASE
			
			WHEN pci.count IS NULL THEN
			0 ELSE pci.count 
		END AS contract_count 
	FROM
		zt_contract_info c
		LEFT JOIN ( SELECT contract_id, count( 1 ) AS count FROM zt_proj_contract_info zpci WHERE zpci.del = 0 GROUP BY contract_id ) pci ON `pci`.`contract_id` = `c`.`id`
		LEFT JOIN (
		SELECT
			pci.contract_id,
			sum( maxpir.total_amount ) AS pir_total_amount 
		FROM
			zt_proj_contract_info pci
			//改为LEFT JOIN进行查询
			LEFT JOIN (
			SELECT * FROM
				zt_proj_invoice_req pir
				LEFT JOIN (
				SELECT
					pf.pid,
					pf.id AS pfid 
				FROM
					zt_proj_form_info pf 
				WHERE
					form_id = 14 
					AND pf.del = 0 
					AND pf.stage = 99 
				) maxpf ON maxpf.pfid = pir.parent_id 
				AND pir.del = 0 
			) AS maxpir ON maxpir.pid = pci.pid 
			//==============
		WHERE
			pci.del = 0 
		GROUP BY
			pci.contract_id 
		) sum_pir ON `sum_pir`.`contract_id` = `c`.`id` 
	WHERE
		`c`.`del` = 0 
		AND ( c.del = 0 AND c.confirm_amount != sum_pir.pir_total_amount OR sum_pir.pir_total_amount IS NULL ) 
ORDER BY
	`c`.`id`

通过explain可以看出前后的差距

修改前:
在这里插入图片描述

修改后:
在这里插入图片描述

<think>我们被要求回答关于MySQL查询优化的问题。根据提供的引用内容,我们可以总结出以下优化方法: 1. 定位慢查询:通过慢查询日志找出执行缓慢的SQL语句。 2. 分析查询:使用EXPLAIN分析SQL语句的执行计划。 3. 优化索引:添加合适的索引,避免全表扫描。 4. 优化查询语句:避免使用SELECT *,只选择需要的列;优化JOIN操作;避免使用子查询改用JOIN;避免在WHERE子句中对字段进行函数操作等。 5. 分页优化:对于大偏移量的分页查询,使用更高效的方法(如记录上次查询的最大ID)。 6. 数据类型优化:使用更小的数据类型,如用TIMESTAMP代替DATETIME,用整数类型代替字符串类型存储IP等。 7. 避免慢查询:定期分析慢查询日志,及时优化。 我们将按照这些方面来组织回答,并参考引用内容进行详细说明。</think>### MySQL查询优化方法详解 MySQL查询优化需结合**索引设计**、**SQL改写**、**配置调整**等多维度进行。以下是系统化的解决方案: --- ### **一、定位慢查询根源** 1. **启用慢查询日志** - 修改配置文件 `my.cnf`: ```ini slow_query_log = 1 slow_query_log_file = /var/log/mysql/slow.log long_query_time = 2 # 定义慢查询阈值(秒) log_queries_not_using_indexes = 1 # 记录未使用索引的查询 ``` - 重启MySQL生效,通过日志分析高频慢SQL[^1][^4]。 --- ### **二、索引优化(核心手段)** 1. **添加缺失索引** - 对 `WHERE`、`JOIN`、`ORDER BY` 字段创建索引: ```sql ALTER TABLE orders ADD INDEX idx_user_id (user_id); -- 为user_id添加索引[^2][^3] ``` - 联合索引需遵循**最左前缀原则**: ```sql ALTER TABLE users ADD INDEX idx_name_age (name, age); -- 索引顺序需匹配查询条件 ``` 2. **避免索引失效场景** - 禁止对索引列进行函数操作: ```sql -- ❌ 错误示例(索引失效) SELECT * FROM users WHERE YEAR(create_time) = 2023; -- ✅ 优化为范围查询 SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-12-31'; ``` - 避免 `LIKE` 以通配符开头: ```sql -- ❌ 索引失效 SELECT * FROM products WHERE name LIKE '%apple%'; -- ✅ 使用全文索引或倒排索引替代 ``` --- ### **三、SQL语句优化** 1. **改写低效语法** - **用 `JOIN` 替代子查询**: ```sql -- ❌ 低效子查询 SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE amount > 100); -- ✅ JOIN优化 SELECT u.* FROM users u JOIN orders o ON u.id = o.user_id WHERE o.amount > 100;[^3] ``` - **避免 `SELECT *`**: ```sql SELECT id, name FROM users; -- 仅查询必要字段[^2] ``` 2. **分页优化** - 大偏移量分页改用 **游标或延迟关联**: ```sql -- ❌ 低效分页(扫描前10000行) SELECT * FROM orders LIMIT 10000, 20; -- ✅ 优化(通过ID定位) SELECT * FROM orders WHERE id > 10000 LIMIT 20; ``` --- ### **四、表结构与数据类型优化** 1. **选择高效数据类型** - 用 `TIMESTAMP`(4字节)替代 `DATETIME`(8字节)[^2] - 金额字段转换为整数存储: ```sql DECIMAL(10,2) → BIGINT -- 存储时乘以100(如1.23元存为123)[^2] ``` 2. **规范表设计** - 避免大字段(如 `TEXT`)影响行存储密度 - 拆分高频更新与低频查询字段到不同表 --- ### **五、配置与系统级优化** 1. **调整关键参数** - 增加 `innodb_buffer_pool_size`(通常设为物理内存70%~80%) - 控制未使用索引的日志量: ```ini log_throttle_queries_not_using_indexes = 100 -- 每分钟最多记录100条[^4] ``` 2. **监控锁竞争** - 通过 `SHOW ENGINE INNODB STATUS` 检查锁等待 - 优化事务隔离级别(如 `READ COMMITTED`)[^4] --- ### **六、持续监控机制** 1. 定期分析慢日志工具(如 `pt-query-digest`) 2. 使用性能诊断工具: - `EXPLAIN` 分析执行计划 - `SHOW PROFILE` 查看资源消耗 > **优化本质**:通过减少磁盘I/O、降低CPU计算量、最小化网络传输提升效率[^1][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值