今天遇到一個SQL優化的問題,記錄下來
測試部門反映,有個功能的查詢很緩慢,需要支持排查下
首先描述下具體的問題
數據庫中一張表的大概有700多條記錄,業務需要模擬一個類似屬性樹形的數據結構,需要查詢所有父類的列表數據,查詢出來符合記錄的大概有400多條,目前測試點擊查詢,所需要的時間大概在4s多,這個效率實在太慢了;
下面是SQL的內容
SELECT
(SELECT
MAX(f.url)
FROM
order_definnition o
JOIN order_def_form_relation d
ON o.id = d.pid
AND d.is_interflow = 1
AND o.is_deleted = 0
AND d.is_deleted = 0
JOIN form f
ON f.id = d.form_id
AND f.is_deleted = 0
WHERE o.resource_code = t.from_oddf_class_no) AS url,
(SELECT
MAX(f.url)
FROM
order_definnition o
JOIN order_def_form_relation d
ON o.id = d.pid
AND d.is_audit = 1
AND o.is_deleted = 0
AND d.is_deleted = 0
JOIN form f
ON f.id = d.form_id
AND f.is_deleted = 0
WHERE o.resource_code = t.from_oddf_class_no) AS source_url,
id
FROM
unicom_send_recv_order t
WHERE is_deleted = 0
AND t.id IN
(SELECT
(SELECT
b.id
FROM
unicom_send_recv_order b
WHERE b.is_deleted = 0
AND b.unicom_first_id = a.unicom_first_id
AND (
b.data_direction = 0
OR (
b.data_direction = 1
AND (
b.order_status = '1001516'
OR b.order_status = '1001515'
)
)
OR b.is_start_line = 1
)
ORDER BY b.send_recv_count DESC
LIMIT 1)
FROM
unicom_send_recv_order a
WHERE a.is_deleted = 0
GROUP BY a.unicom_first_id)
ORDER BY t.created_time DESC
這個sql的邏輯,在滿足一定的條件下,選擇send_recv_count
最大的那條記錄的id來展示。
添加索引
在不影響業務的條件下,我們通過添加索引來試試,查看錶中的關聯字段,我們需要對如下的字段添加索引
ALTER TABLE `sceo`.`unicom_send_recv_order`
ADD INDEX `index_unicom_first_id` (`unicom_first_id`),
ADD INDEX `index_from_oddf_class_no` (`from_oddf_class_no`),
ADD INDEX `index_send_recv_count` (`send_recv_count`);
ALTER TABLE `sceo`.`order_def_form_relation`
ADD INDEX `index_pid` (`pid`),
ADD INDEX `index_form_id` (`form_id`);
再次執行該上面的sql語句,發現查詢的效率變快了了,耗時2700+ms數,這個時間還是滿足不了業務的需要
重構sql
分析上述的sql,發現在子查詢中通過排序來獲取send_recv_count
最大的記錄,然後再分組查詢,這個是主要導致查詢效率緩慢的原因,我們可以改寫sql語句來減少分組和排序
我們利用變量來爲排序標記,然後獲取變量爲1的記錄,利用這種方式來避免排序分組, 重構的sql如下:
SELECT
(SELECT
MAX(f.url)
FROM
order_definnition o
JOIN order_def_form_relation d
ON o.id = d.pid
AND d.is_interflow = 1
AND o.is_deleted = 0
AND d.is_deleted = 0
JOIN form f
ON f.id = d.form_id
AND f.is_deleted = 0
WHERE o.resource_code = t.from_oddf_class_no) AS url,
(SELECT
MAX(f.url)
FROM
order_definnition o
JOIN order_def_form_relation d
ON o.id = d.pid
AND d.is_audit = 1
AND o.is_deleted = 0
AND d.is_deleted = 0
JOIN form f
ON f.id = d.form_id
AND f.is_deleted = 0
WHERE o.resource_code = t.from_oddf_class_no) AS source_url, id
FROM
unicom_send_recv_order t
WHERE is_deleted = 0
AND EXISTS
(SELECT tt.id from (SELECT
b.id,
b.unicom_first_id,
send_recv_count,
IF (
@pre_course_id = b.unicom_first_id,
@cur_rank := @cur_rank + 1,
@cur_rank := 1
) ranking,
@pre_course_id := b.unicom_first_id
FROM
unicom_send_recv_order b,
(SELECT
@cur_rank := 0,
@pre_course_id := NULL
) r
where b.is_deleted = 0
AND (b.data_direction = 0
OR (b.data_direction = 1
AND (b.order_status = '1001516'
OR b.order_status = '1001515'))
OR b.is_start_line = 1)
ORDER BY
unicom_first_id,
send_recv_count DESC) tt where tt.ranking = 1
AND tt.id = t.id)
ORDER BY t.created_time DESC
在重構後的sql中,可以看到我們排序之後的記錄添加一個序列,然後獲取序列的第一條來作爲我們現實的記錄,然後將in
改爲exists
,這樣優化後的查詢就變得效率很高了,經過測試,耗時降到了200ms,這個速度暫時是可以接受的了