2012-12-08 230 views
8

我創建了2個可用於執行相同功能的查詢。它們都包含我想合併成單個查詢的屬性,但我一直無法。使用子查詢與左連接的MySQL索引優化

查詢1 - 給我的結果我想要的。慢(〜0.700秒)

查詢2 - 給我很多行,我忽略和跳過。快速(〜0.005秒)

我的目標是修改QUERY 2,爲每個項目刪除除1之外的所有空行價格行。我似乎無法完成這項工作,因爲我們對錶現不感興趣。這是由於我缺乏對MySQL中索引使用的經驗和理解。

QUERY 1

用途設計不良的子查詢不允許跨tbl_sale(e)中使用索引包含10K行。

SELECT b.id, b.sv, b.description, der.store_id, f.name, der.price 
FROM tbl_watch AS a 
    LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN (
    SELECT c.store_id, d.flyer_id, e.item_id, e.price 
    FROM tbl_storewatch AS c, tbl_storeflyer AS d 
    FORCE INDEX (storebeg_ndx) , tbl_sale AS e 
    WHERE c.user_id = '$user_id' 
    AND (
     d.store_id = c.store_id 
     AND d.date_beg = '20121206' 
     ) 
    AND e.flyer_id = d.flyer_id 
     ) AS der ON a.item_id = der.item_id 
LEFT JOIN tbl_store as f ON der.store_id = f.id 
WHERE a.user_id = '$user_id' 
ORDER BY b.description ASC 

這裏是解釋查詢1

id select_type table  type possible_keys key    key_len  ref  rows Extra 
1 PRIMARY  a   ref  user_item_ndx user_item_ndx 4   const 30 Using index; Using temporary; Using filesort 
1 PRIMARY  b   eq_ref PRIMARY   PRIMARY   4   a.item_id 1 
1 PRIMARY  <derived2> ALL  NULL   NULL   NULL  NULL 300  
1 PRIMARY  f   eq_ref PRIMARY   PRIMARY   4   der.store_id 1 
2 DERIVED  c   ref  user_ndx  user_ndx  4     6 
2 DERIVED  e   ALL  NULL   NULL NULL NULL    9473 Using join buffer 
2 DERIVED  d   eq_ref storebeg_ndx storebeg_ndx 8   c.store_id 1 Using where 

QUERY 2

全部使用左聯接,這是非常有效的(與ORDER BY除外)。索引用於每個連接。該查詢返回tbl_watch中每個項目的所有可能匹配項。下面是該查詢:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
LEFT JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
    AND d.date_beg = '$s_date' 
LEFT JOIN tbl_sale AS e ON e.item_id = a.item_id 
    AND e.flyer_id = d.flyer_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
ORDER BY b.description ASC 

這裏是解釋查詢:

id select_type  table type possible_keys   key    key_len  ref      rows Extra 
1 SIMPLE   a  ref  user_item_ndx   user_item_ndx 4   const     6  Using index; Using temporary; Using filesort 
1 SIMPLE   b  eq_ref PRIMARY     PRIMARY   4   a.item_id    1 
1 SIMPLE   c  ref  user_ndx    user_ndx  4   const     2 
1 SIMPLE   d  eq_ref storebeg_ndx,storendx storebeg_ndx 8   c.store_id,const  1 
1 SIMPLE   e  eq_ref itemflyer_ndx   itemflyer_ndx 8   a.item_id,d.flyer_id 1 
1 SIMPLE   f  eq_ref PRIMARY     PRIMARY   4   d.store_id    1 

如何修改問題2(更有效)給我正是我需要像在查詢1對行與嗎?

感謝 邁克

+0

我不太確定第一個查詢可能會給你想要的結果。左連接不是左外連接(雖然也許它在MySQL中,它不符合SQL),並且空值不是唯一值。我沒有方便的MySQL,但把它放到PostgreSQL中並沒有給出你描述的結果。我的回答如下... – PlexQ

回答

0

你的子查詢中查詢1使用隱式內部聯接,而查詢2是使用所有連接左明確連接。因此,有沒有where子句中查詢2.排除數據我會採取左側開出的一對夫婦行(標示),看看如何改善的事情:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
-- Left removed below 
JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
    AND d.date_beg = '$s_date' 
-- Left removed below 
JOIN tbl_sale AS e ON e.item_id = a.item_id 
    AND e.flyer_id = d.flyer_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
ORDER BY b.description ASC` 

您也可以考慮服用並把它們移出來並移動到WHERE:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
JOIN tbl_sale AS e ON e.item_id = a.item_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
AND d.date_beg = '$s_date' 
AND e.flyer_id = d.flyer_id 
ORDER BY b.description ASC 

最後,日期數學相當密集。在查詢2中,使用外部連接,可以避免很多,但可能需要它。我想嘗試通過使用子查詢得到的ID和限制:

SELECT b.id, b.sv, b.description, c.store_id, f.name, e.price 
FROM tbl_watch AS a 
LEFT JOIN tbl_item AS b ON a.item_id = b.id 
LEFT JOIN tbl_storewatch AS c ON c.user_id = '$user_id' 
JOIN tbl_storeflyer AS d ON d.store_id = c.store_id 
JOIN tbl_sale AS e ON e.item_id = a.item_id 
LEFT JOIN tbl_store as f ON d.store_id = f.id 
WHERE a.user_id = '$user_id' 
AND e.flyer_id = d.flyer_id 
AND d.id in (select d.id from d where date_beg = '$s_date') 
ORDER BY b.description ASC 
+0

謝謝你的迴應!這些解決方案確實爲所有具有活動銷售項目的項目(e.item_id = a.item_id AND e.flyer_id = d.flyer_id)提供行,但我也試圖將每個項目包含在(b)域中的tbl_watch(a)中,即使它們不存在於tbl_sale(e)中。所以我最終會得到:id,sv,description,NULL,NULL,NULL。我只想讓每行有NULL的行。我不知道如何完成這一點。 – ridgeback

+0

只是爲了澄清我期望每件物品都屬於以下三種情況之一:1 - 具有單一價格的物品。 2 - 具有多個價格的項目。 3 - 沒有價格的項目。如果發生3,我仍然希望返回一個包含項目ID,sv和描述的行。 – ridgeback

+0

不確定date_beg這裏是一個實際的日期字段,看起來它被用作某種字符。我不確定日期匹配在MySQL中很慢,但我會感到驚訝。日期通常是在內部長時間存儲的,唯一的花費是將該字符串轉換爲長整型,所以我不相信它會增加任何開銷。 – PlexQ

1

我覺得這個查詢會給你想要的東西:

select a.id, a.sv, a.description, c.id, c.name, b.price 
    from 
    tbl_item a left outer join tbl_sale b on (a.id=b.item_id) 
     left outer join tbl_storeflyer d on (b.flyer_id=d.flyer_id and d.date_beg = '20120801') 
     left outer join tbl_store c on (d.store_id = c.id) 
     left outer join tbl_storewatch x on (c.id = x.store_id) 
     left outer join tbl_watch y on (a.id = y.item_id); 

與參與NULL值,你有可能會有一些左連接。替代的方法是使用一個聯盟,這與MySQL可能會更快:

select a.id, a.sv, a.description, c.id as store_id, c.name, b.price 
    from 
    tbl_item a, 
    tbl_sale b, 
    tbl_storeflyer d, 
    tbl_store c, 
    tbl_storewatch x, 
    tbl_watch y 
    where 
    a.id = b.item_id and 
    b.flyer_id = d.flyer_id and 
    d.store_id = c.id and 
    c.id = x.store_id and 
    a.id = y.item_id and 
    d.date_beg = '20120801' 
union 
select a.id, a.sv, a.description, null as store_id, null as name, null as price 
    from 
    tbl_item a 
    where 
    a.id not in (select b.item_id from tbl_sale b); 

你可能會和工會作爲一個左外下半場打加入,而不是一個「不」子查詢 - 取決於如何你的MySQL版本會優化。