最近因爲項目很趕,很久沒寫博客了,今天算是已經把項目進度超前完成了,寫一個今天解決的SQL優化問題。
這次優化花了半小時,但很值得,之前作爲一個單純後端,很少關注SQL效率,但是自從上個項目當上臨時DBA後,或多或少的都會關注自己書寫的SQL執行計劃。
原始第一映像寫出的腳本:
EXPLAIN
SELECT
service_id,service_name,count(1)
FROM
order_orders t1,
order_user_coupon t2
WHERE
t1.coupon_id = t2.coupon_id
AND t1.merchant_id = '1'
AND (
t1.update_time BETWEEN str_to_date(
'2019-01-01 00:00:00',
'%Y-%m-%d %H:%i:%s'
)
AND str_to_date(
'2019-03-06 00:00:00',
'%Y-%m-%d %H:%i:%s'
)
)
group by service_id;
執行計劃如下:
兩張表各種加索引,沒用,後來仔細分析了一下,之所以產生臨時表,是因爲整個查詢過程,你首先要通過關聯條件查詢出一個結果集,然後再根據這個結果集進行分組排序,自然就會有臨時表和文件排序。
第二次優化腳本如下:
EXPLAIN SELECT
t1.service_id,
t1.service_name,
count(1)
FROM
order_user_coupon t1
WHERE
t1.coupon_id in(
SELECT
coupon_id
FROM
order_orders t2
WHERE
t2.merchant_id = '1'
AND t2.update_time BETWEEN str_to_date(
'2019-01-01 00:00:00',
'%Y-%m-%d %H:%i:%s'
)
AND str_to_date(
'2019-03-06 00:00:00',
'%Y-%m-%d %H:%i:%s'
)
)
GROUP BY
t1.service_id;
本想着,避免了關聯查詢,使用t1表的結果集做分組,應該能避免臨時表及文件排序,結果沒想到in中的查詢使用了,好吧,廢棄。
第三次優化如下:
EXPLAIN SELECT
t1.service_id,
t1.service_name,
count(1)
FROM
order_user_coupon t1
WHERE
EXISTS (
SELECT
coupon_id
FROM
order_orders t2
WHERE
t1.coupon_id = t2.coupon_id
AND t2.merchant_id = '1'
AND t2.update_time BETWEEN str_to_date(
'2019-01-01 00:00:00',
'%Y-%m-%d %H:%i:%s'
)
AND str_to_date(
'2019-03-06 00:00:00',
'%Y-%m-%d %H:%i:%s'
)
)
GROUP BY
t1.service_id;
思路:以條件篩選出t2表中數據,以少量數據作爲驅動,查詢t1表關聯數據,之後基於t1表的結果集進行group by操作。
完美解決!
最後優化索引:
1、 group by 後的字段一定是要添加索引的,否則還是 臨時表進行排序;
2、 這裏merchant_id基本能完成大部分數據篩選了,所以添加merchant_id的索引,而不再添加coupon_id索引。
最終結果如下:
Using Index Condition
在MySQL 5.6版本後加入的新特性(Index Condition Pushdown);會先條件過濾索引,過濾完索引後找到所有符合索引條件的數據行,隨後用 WHERE 子句中的其他條件去過濾這些數據行;
夠用了,完美!