這篇博文講述如何優化JOIN查詢帶有排序的情況。大致分為對連接屬性排序和對非連接屬性排序兩種情況。插入測試數據。
CREATE TABLE t1 ( id INT PRIMARY KEY AUTO_INCREMENT, type INT ); SELECT COUNT(*) FROM t1; +----------+ | COUNT(*) | +----------+ | 10000 | +----------+ CREATE TABLE t2 ( id INT PRIMARY KEY AUTO_INCREMENT, type INT ); SELECT COUNT(*) FROM t2; +----------+ | COUNT(*) | +----------+ | 100 | +----------+
對連接屬性進行排序
現要求對t1和t2做內連接,連接條件是t1.id=t2.id,並對連接屬性id屬性進行排序(MySQL為主鍵id建立了索引)。
有兩種選擇,方式一[...ORDER BY t1.id],方式二[...ORDER BY t2.id],選哪種呢?
首先我們找出驅動表和被驅動表,按照小表驅動大表的原則,大表是t1,小表是t2,所以t2是驅動表,t1是非驅動表,t2驅動t1。然后進行分析,如果我們使用方式一的話,MySQL會先對t1進行排序然后執行表連接算法,如果我們使用方式二的話,只能執行表連接算法后對結果集進行排序(extra:using temporary),效率必然低下。
所以,當對連接屬性進行排序時,應當選擇驅動表的屬性作為排序表中的條件。
-- 對被驅動表字段進行排序 EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON t1.id =t2.id ORDER BY t1.id; +----+-------+--------+---------+------+---------------------------------+ | id | table | type | key | rows | Extra | +----+-------+--------+---------+------+---------------------------------+ | 1 | t2 | ALL | NULL | 100 | Using temporary; Using filesort | | 1 | t1 | eq_ref | PRIMARY | 1 | NULL | +----+-------+--------+---------+------+---------------------------------+ -- 對驅動表字段進行排序,沒有Using temporary,也沒有Using filesort EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON t1.id =t2.id ORDER BY t2.id; +----+-------+--------+---------+------+-------+ | id | table | type | key | rows | Extra | +----+-------+--------+---------+------+-------+ | 1 | t2 | index | PRIMARY | 100 | NULL | | 1 | t1 | eq_ref | PRIMARY | 1 | NULL | +----+-------+--------+---------+------+-------+
對非連接屬性進行排序
現要求對t1和t2做內連接,連接條件是t1.id=t2.id,並對非連接屬性t1的type屬性進行排序,[...ORDER BY t1.type]。
首先我們找出驅動表和被驅動表,按照小表驅動大表的原則,大表是t1,小表是t2,所以MySQL Optimizer會用t2驅動t1。現在我們要對t1的type屬性進行排序,t1是被驅動表,必然導致對連接后結果集進行排序Using temporary(比Using filesort更嚴重)。所以,能不能不用MySQL Optimizer,用大表驅動小表呢?
有請STRAIGHT_JOIN!
EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON t1.id =t2.id ORDER BY t1.type; +----+-------+--------+---------+------+---------------------------------+ | id | table | type | key | rows | Extra | +----+-------+--------+---------+------+---------------------------------+ | 1 | t2 | ALL | NULL | 100 | Using temporary; Using filesort | | 1 | t1 | eq_ref | PRIMARY | 1 | NULL | +----+-------+--------+---------+------+---------------------------------+
-- Using temporary沒有了,但是大表驅動小表,導致內循環次數增加,實際開發中要從實際出發, -- 對此作出權衡。 EXPLAIN SELECT * FROM t1 STRAIGHT_JOIN t2 ON t1.id =t2.id ORDER BY t1.type; +----+-------+--------+---------+-------+----------------+ | id | table | type | key | rows | Extra | +----+-------+--------+---------+-------+----------------+ | 1 | t1 | ALL | NULL | 10000 | Using filesort | | 1 | t2 | eq_ref | PRIMARY | 1 | NULL | +----+-------+--------+---------+-------+----------------+
最后在MySQL的JOIN(一):用法那里挖了個坑,現在填上:INNER JOIN、JOIN、WHERE等值連接和STRAIGHT_JOIN都能表示內連接,那平時如何選擇呢?一般情況下用INNER JOIN、JOIN或者WHERE等值連接,因為MySQL Optimizer會按照“小表驅動大表的策略”進行優化。當出現上述問題時,才考慮用STRAIGHT_JOIN
總結
《MySQL的JOIN》到此為止。
這系列博文講述了JOIN的用法,JOIN的原理,以及在JOIN原理的基礎上進行優化的手段。希望對大家有幫助吧:)