记一次sql优化,in+子查询

阅读更多
拿到一个sql,同事告诉我这个sql索引加了,为什么还是这么慢。
sale_order_goods,sale_order 两张表都有几十万的数据。其中in结果集有30万。

sql如下:
SELECT	
	ifnull(sum(buy_number), 0) AS buy_number_sum
FROM
	sale_order_goods
WHERE
	sale_order_id IN (
		SELECT
			so.id 

		FROM
			sale_order_goods sog,
			sale_order so
		WHERE
			1 = 1
		AND sog.tenant_org_id =1
		AND so.tenant_org_id =1
		AND sog.sale_order_id = so.id 

		AND so.order_status =1
		AND DATE_SUB(CURDATE(), INTERVAL 30 DAY) <= date(sog.created_at)
	);


这是一个in + 子查询的语句。

顺便提一句,innodb对in + 子查询的处理非常差,比如:
select * from a where a.oid in (select id from b where b.name='xxx')
,如果
select id from b where b.name='xxx'
返回的结果是1,2,3。那么sql是不是就变成了
select * from a where a.oid in (1,2,3)
. NO!!,事实上不是这样。innodb会把外层表压入到子查询中,sql会变成
select * from a exists (select * from b where name='xxx' and b.id = a.oid)

这时,子查询关联外部表a,所以innodb认为无法执行这个子查询,于是需要全表遍历一遍a,再根据a返回的oid逐个执行一次子查询。

上面这个sql不需要执行子查询,改写如下:
SELECT
			ifnull(sum(buy_number), 0) AS buy_number_sum
		FROM
			sale_order_goods sog,
			sale_order so
		WHERE
			1 = 1
		AND sog.tenant_org_id =1
		AND so.tenant_org_id =1
		AND sog.sale_order_id = so.id 

		AND so.order_status =1
		AND DATE_SUB(CURDATE(), INTERVAL 30 DAY) <= date(sog.created_at)


直接放弃 in + 子查询的方式。sql在1秒内执行完毕。

你可能感兴趣的:(sql)