2010-05-27 41 views
1

我試圖優化以下查詢:MySQL查詢優化 - 不同,ORDER BY和限制

select distinct this_.id as y0_ 
from Rental this_ 
    left outer join RentalRequest rentalrequ1_ 
     on this_.id=rentalrequ1_.rental_id 
    left outer join RentalSegment rentalsegm2_ 
     on rentalrequ1_.id=rentalsegm2_.rentalRequest_id 
where 
    this_.DTYPE='B' 
    and this_.id<=1848978 
    and this_.billingStatus=1 
    and rentalsegm2_.endDate between 1273631699529 and 1274927699529 
order by rentalsegm2_.id asc 
limit 0, 100; 

該查詢在一排的記錄分頁處理完成的多個時間(每個不同的時間限制)。它返回處理中我需要的ID。我的問題是這個查詢需要超過3秒鐘。我在三張桌子的每張桌上都有大約200萬行。

給出解釋:

+----+-------------+--------------+--------+-----------------------------------------------------+---------------+---------+--------------------------------------------+--------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys          | key   | key_len | ref          | rows | Extra          | 
+----+-------------+--------------+--------+-----------------------------------------------------+---------------+---------+--------------------------------------------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | rentalsegm2_ | range | index_endDate,fk_rentalRequest_id_BikeRentalSegment | index_endDate | 9  | NULL          | 449904 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | rentalrequ1_ | eq_ref | PRIMARY,fk_rental_id_BikeRentalRequest    | PRIMARY  | 8  | solscsm_main.rentalsegm2_.rentalRequest_id |  1 | Using where         | 
| 1 | SIMPLE  | this_  | eq_ref | PRIMARY,index_billingStatus       | PRIMARY  | 8  | solscsm_main.rentalrequ1_.rental_id  |  1 | Using where         | 
+----+-------------+--------------+--------+-----------------------------------------------------+---------------+---------+--------------------------------------------+--------+----------------------------------------------+ 

我試圖刪除不同和查詢的運行速度提高三倍。沒有查詢說明給出:

+----+-------------+--------------+--------+-----------------------------------------------------+---------------+---------+--------------------------------------------+--------+-----------------------------+ 
| id | select_type | table  | type | possible_keys          | key   | key_len | ref          | rows | Extra      | 
+----+-------------+--------------+--------+-----------------------------------------------------+---------------+---------+--------------------------------------------+--------+-----------------------------+ 
| 1 | SIMPLE  | rentalsegm2_ | range | index_endDate,fk_rentalRequest_id_BikeRentalSegment | index_endDate | 9  | NULL          | 451972 | Using where; Using filesort | 
| 1 | SIMPLE  | rentalrequ1_ | eq_ref | PRIMARY,fk_rental_id_BikeRentalRequest    | PRIMARY  | 8  | solscsm_main.rentalsegm2_.rentalRequest_id |  1 | Using where     | 
| 1 | SIMPLE  | this_  | eq_ref | PRIMARY,index_billingStatus       | PRIMARY  | 8  | solscsm_main.rentalrequ1_.rental_id  |  1 | Using where     | 
+----+-------------+--------------+--------+-----------------------------------------------------+---------------+---------+--------------------------------------------+--------+-----------------------------+ 

正如你所看到的,Using temporary使用不同的時加入。

我已經有了where子句中使用的所有字段的索引。 有什麼我可以做的,以優化此查詢?

非常感謝!

編輯:我試着按照建議的順序在this_.id上查詢,查詢速度慢了5倍。這裏是解釋計劃:

+----+-------------+--------------+------+-----------------------------------------------------+---------------------------------------+---------+------------------------------+--------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys          | key         | key_len | ref       | rows | Extra          | 
+----+-------------+--------------+------+-----------------------------------------------------+---------------------------------------+---------+------------------------------+--------+----------------------------------------------+ 
| 1 | SIMPLE  | this_  | ref | PRIMARY,index_billingStatus       | index_billingStatus     | 5  | const      | 782348 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | rentalrequ1_ | ref | PRIMARY,fk_rental_id_BikeRentalRequest    | fk_rental_id_BikeRentalRequest  | 9  | solscsm_main.this_.id  |  1 | Using where; Using index; Distinct   | 
| 1 | SIMPLE  | rentalsegm2_ | ref | index_endDate,fk_rentalRequest_id_BikeRentalSegment | fk_rentalRequest_id_BikeRentalSegment | 8  | solscsm_main.rentalrequ1_.id |  1 | Using where; Distinct      | 
+----+-------------+--------------+------+-----------------------------------------------------+---------------------------------------+---------+------------------------------+--------+----------------------------------------------+ 

回答

2
  1. 從我們看到優化是足夠聰明,明白,你不需要外部連接這裏的執行計劃。無論如何,你應該明確指出。
  2. DISTINCT修飾符表示您希望GROUP BY中的所有字段,即所有指定字段的ORDER BY,然後丟棄重複項。換句話說,order by rentalsegm2_.id asc子句在這裏沒有任何意義。

查詢下面應該返回相同的結果:

select distinct this_.id as y0_ 
from Rental this_ 
    join RentalRequest rentalrequ1_ 
     on this_.id=rentalrequ1_.rental_id 
    join RentalSegment rentalsegm2_ 
     on rentalrequ1_.id=rentalsegm2_.rentalRequest_id 
where 
    this_.DTYPE='B' 
    and this_.id<=1848978 
    and this_.billingStatus=1 
    and rentalsegm2_.endDate between 1273631699529 and 1274927699529 
limit 0, 100; 

UPD

如果你想執行計劃入手RentalSegment,則需要以下指標添加到數據庫:

  1. RentalSegment(endDate)
  2. RentalRequest(ID,rental_id)
  3. 租賃(ID,DTYPE,billingStatus)或(ID,billingStatus,DTYPE)

查詢然後可以被改寫爲下面:

SELECT this_.id as y0_ 
FROM RentalSegment rs 
    JOIN RentalRequest rr 
    JOIN Rental this_ 
WHERE rs.endDate between 1273631699529 and 1274927699529 
    AND rs.rentalRequest_id = rr.id 
    AND rr.rental_id <= 1848978 
    AND rr.rental_id = this_.id 
    AND this_.DTYPE='D' 
    AND this_.billingStatus = 1 
GROUP BY this_.id 
LIMIT 0, 100; 

如果執行計劃不會從RentalSegment開始,您可以強制執行STRAIGHT_JOIN

+0

我不明白爲什麼順序沒有意義。由於這是一個分頁標準(我將增加每個調用的第一個參數限制),所以我必須指定一個oder,否則結果不會總是以相同順序返回,並且分頁不起作用。我錯過了什麼嗎? – 2010-05-27 12:37:29

+0

@Manuel Darveau:在SELECT部分​​中沒有提及'resesegm2_.id'字段,並且可能與this._id有一對多的關係。當GROUP BY'this._id'時,'rentalsegm2_.id'進行排序不再有意義,因爲執行排序的項目可以任意進行。 GROUPING操作(和DISTINCT)假定按分組列進行隱式排序(您甚至可以在MySQL中說'GROUP BY this_id DESC'),並按照排序順序執行結果。 – newtover 2010-05-27 12:46:54

+0

@Manuel Darveau:我的意思是說'rentalsegm2_.id'的排序不會使sence,而是'this_.id asc'顯式排序(在這種情況下是隱含的)。 – newtover 2010-05-27 12:53:40

2

沒有獨立運行的查詢運行得更快的原因是因爲您有限制子句。如果沒有明確的規定,服務器只需要查看前一百場比賽。然而,其中一些行可能有重複的字段,所以如果你引入了distinct子句,服務器必須查看更多的行才能找到沒有重複值的行。

順便說一句,你爲什麼使用OUTER JOIN?

+0

@Manuel Darveau:的確,爲什麼要使用OUTER JOIN。您是否使用「正常」連接嘗試/驗證了結果?應該快得多! – lexu 2010-05-27 05:20:36

1

在這裏,對於「rentalsegm2_」表,優化器選擇了「index_endDate」索引,其預計從該表得到的行數大約爲4.5萬億。由於存在其他條件,您可以檢查「this_」表索引。我的意思是你可以在「this_table」中檢查每個條件受影響的記錄數量。

總之,您可以通過更改優化器使用的索引來嘗試替代解決方案。 這可以通過「USE INDEX」,「FORCE INDEX」命令獲得。

感謝

Rinson KE DBA