2015-07-20 209 views
1

我需要幫助,在一個LEFT JOIN的SQL查詢中選擇正確的索引。MySQL查詢優化:正確索引

我有兩個表(對象和月度報告):

CREATE TABLE some_objects (
    id int(11) NOT NULL AUTO_INCREMENT, 
    ..., 
    PRIMARY KEY (id), 
); 
CREATE TABLE month_reports (
    id int(11) NOT NULL AUTO_INCREMENT, 
    some_object_id int(11) DEFAULT NULL, 
    month int(11) NOT NULL, 
    year int(11) NOT NULL, 
    ..., 
    PRIMARY KEY (id), 
    INDEX IDX_123 (month, year), 
    INDEX IDX_456 (some_object_id), 
    INDEX IDX_789 (some_object_id, month, year), 
    CONSTRAINT FK_123 FOREIGN KEY (some_object_id) 
    REFERENCES some_objects (id) ON DELETE RESTRICT ON UPDATE RESTRICT 
) 

我需要選擇與特定月份或沒有他們報告的所有對象。我想這個查詢:

SELECT * FROM some_objects so 
LEFT JOIN month_reports mr ON mr.some_object_id = so.id 
WHERE 
    mr.month = :month 
    AND mr.year = :year 
    OR mr.id IS NULL 

EXPLAIN結果:

id select_type table type possible_keys 
1 SIMPLE  so  ALL 
1 SIMPLE  mr  ALL IDX_456,IDX_789 

EXPLAIN結果,而不OR mr.id IS NULL

id select_type table type possible_keys   key  ref 
1 SIMPLE  mr  ref  IDX_123,IDX_456,IDX_789 IDX_123 const,const 
1 SIMPLE  so  eq_ref PRIMARY 

但是,如果沒有OR mr.id IS NULL我不無報告得到的對象!

我應該添加哪個其他索引?我是否需要更改查詢?

P.S.對不起我的英語不好。

UPD。ANALYZE TABLE返回「狀態正常」。 EXPLAIN結果與FORCE INDEX(10個對象,只有一個有報告):

id select_type table type possible_keys key  rows 
1 SIMPLE  so  ALL       10 
1 SIMPLE  mr  ref IDX_789  IDX_789 1 
+0

您可以添加'some_object_id'現有'IDX_123'。 – Vatev

+0

@Vatev對不起,我修正了IDX_789。它具有some_object_id,month,year列,並且它不被MySQL服務器使用:( –

+0

它有時候會在你的行數太少時添加一些數據(如果數量很少,請添加一些)嘗試運行[ANALYZE TABLE](https:// dev.mysql.com/doc/refman/5.7/en/analyze-table.html)如果這樣做不起作用,可以在JOIN中添加'FORCE KEY(IDX_789)'併發布查詢計劃。 – Vatev

回答

0

這是一個再形成是可能做你想要什麼:

SELECT so.* 
    FROM some_objects so 
    LEFT JOIN month_reports mr 
      ON mr.some_object_id = so.id 
      AND (mr.month = :month AND mr.year = :year) 

注意,它避免了IS NULL處理的,因爲LEFT方式作品。但是,它通過將WHERE移動到ON中稍微改變了語義。

使用UNION代替OR也可能工作(假設框架將讓你這麼做)以下reforumlation:

 (SELECT so.* 
      FROM some_objects so 
      JOIN month_reports mr ON mr.some_object_id = so.id 
      WHERE mr.month = :month 
       AND mr.year = :year   
    ) 
UNION 
     (SELECT so.* 
      FROM some_objects so 
      LEFT JOIN month_reports mr ON mr.some_object_id = so.id 
      WHERE mr.id IS NULL 
    ) 
0

好吧,我沒有得到我所需要的。我會考慮如何以另一種方式解決我的問題。

不幸的是,我不能在我的框架中使用FORCE INDEX。但是我需要獲取所有對象,即使它們都沒有報告。

我只是想優化我的SQL查詢。在一個查詢中這樣做是不成功的嘗試:)