MYSQL優化派生表(子查詢)在From語句中的


      Mysql 在5.6.3中,優化器更有效率地處理派生表(在from語句中的子查詢):

      優化器推遲物化子查詢在from語句中的子查詢,知道子查詢的內容在查詢正真執行需要時,才開始物化。這一舉措提高了性能:

 

      1:之前版本(5.6.3),from語句中的子查詢在explain select 查看執行計划語句執行時就會物化。它導致了部分查詢執行,但explain語句的目的是獲取執行計划信息,而不是執行查詢

該版本物化不會在explain中發生,所以explain執行計划結果的得到更快;

      2:因為上面提及的,物化子查詢的推遲有可能不會發生。考慮一個from語句中的子查詢的結果和另一個表join(鏈接)查詢,如果優化器先處理另一張表A,然后發現A中沒有滿足條件的行返回,此時join不會再繼續執行,並且優化器會完全跳過物化子查詢這步驟;

      考慮下面的explain語句,子查詢出現在form語句中;

   

EXPLAIN SELECT * FROM (SELECT * FROM t1) AS derived_t1;

      優化器避免物化子查詢直到子查詢的結果在查詢真正執行需要時。在上面情況下,查詢並沒有執行,所有並沒有物化(子查詢);

      即使查詢執行時,也會要求優化器去避免物化。考慮下面的查詢:

SELECT *
  FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2 ON t1.f2=derived_t2.f1
  WHERE t1.f1 > 0;

     如果優化器先處理t1,並且where語句產生個空集,join結果必定是空集並且該子查詢沒必要去物化。

     最壞的情況(派生表被物化),查詢執行會花費和之前版本之前的時間,在最好的情況下(派生表不會物化),查詢執行更快。

     在派生表需要物化的情況下,優化器會通過為物化表添加索引來加速訪問物化表的結果,如果添加的索引允許ref方式訪問該物化表,會更好的減少在查詢執行時讀取的數據量。考慮下面的查詢:

SELECT *
 FROM t1 JOIN (SELECT DISTINCT f1 FROM t2) AS derived_t2
      ON t1.f1=derived_t2.f1;

     優化器在derived_t2派生表f1列構造一個索引,如果這樣允許ref方式來最小化執行計划的話費,添加索引,優化器對待物化的派生表和平常的帶index表一樣,添加index的花費比起查詢沒有Index的執行無不足道,如果ref方式比其他訪問方式花費更多,index不會添加,優化器不會做任何優化。

<======================分割線==================================>

From子查詢形式:

SELECT ... FROM (subquery) [AS] name ...

 [AS] Name 語句是強制的(必須加上的), 因為From語句中每個表必須有一個名字. 子查詢中的每個select列必須有一個唯一的名字。

CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT);
INSERT INTO t1 VALUES (1,'1',1.0);
INSERT INTO t1 VALUES (2,'2',2.0);
SELECT sb1,sb2,sb3
  FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb
  WHERE sb1 > 1;

   Result: 2, '2', 4.0.

   另一個例子:目的是想得到一組集合和的平均值。 下面的語句並不起作用:

SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1;

正確格式:

SELECT AVG(sum_column1)
  FROM (SELECT SUM(column1) AS sum_column1
        FROM t1 GROUP BY column1) AS t1;

      From語句中的子查詢會返回一個標量一列,一行或者一個表。from語句中的子查詢不能是相關子查詢,除非有on 或者join關鍵字.

      在Mysql5.6.3之前,from語句中的子查詢會在explain語句中執行(派生臨時表會被物化)。因為上層查詢需要得到所有表的信息在優化階段。在某些情況下用explain select語句會修改表數據,如果外部查詢訪問所有表,並且內部查詢調用一個修改表數據行的存儲方法,見下:

mysql> create table t1(c1 int);
Query OK, 0 rows affected (0.39 sec)

mysql> create table t2(c1 int);
Query OK, 0 rows affected (0.26 sec)

   現在創建一個修改t2表的存儲方法;

mysql> delimiter //
mysql> create function f1(p1 int) returns int
    ->   begin
    ->       insert into t2 values(p1);
    ->       return p1;
    ->   end //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;

   直接引用該存儲方法在一個explain select 語句中不會對t2有任何作用;

mysql> select * from t2;
Empty set (0.00 sec)

mysql> explain select f1(5);
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set (0.00 sec)

 

    這是因為select語句沒用引用任何表, 可以在table和extra列中輸出看到:

mysql> explain select now() as a1,(select f1(5)) as a2;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+-------+------+------------------------------------------+
| Level | Code | Message                                  |
+-------+------+------------------------------------------+
| Note  | 1249 | Select 2 was reduced during optimization |
+-------+------+------------------------------------------+
1 row in set (0.00 sec)

 

    然而, 當外部select引用任何表時,優化器會執行子查詢:

mysql> explain select * from t1 as a1,(select f1(5)) as a2;
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
| id | select_type | table      | type   | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL | NULL    | NULL |    1 | NULL           |
|  1 | PRIMARY     | a1         | ALL    | NULL          | NULL | NULL    | NULL |    1 | NULL           |
|  2 | DERIVED     | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+----+-------------+------------+--------+---------------+------+---------+------+------+----------------+
3 rows in set (0.02 sec)

mysql> select * from t2;
+------+
| c1   |
+------+
|    5 |
+------+
1 row in set (0.00 sec)

 

    這也意味着一個explain select語句(如上)也會需要一段時間來執行.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM