2012-12-08 14 views
8

同じ関数を使用できる2つのクエリを作成しました。それらは両方とも私が単一のクエリにマージしたいプロパティを含んでいますが、私はできませんでした。サブクエリと左結合を使用したMySQLインデックスの最適化

QUERY 1 - 私が欲しい結果を正確に教えてくれます。遅い(〜0.700秒)

QUERY 2 - 私が無視してスキップする行がたくさんあります。速い(〜0.005秒)

私の目標は、項目ごとに1を除くすべてのヌル価格行を削除するようにQUERY 2を変更することです。私はパフォーマンスに苦労してこれをやっているようには思えません。これは私の経験不足とMySQLでのインデックス使用の理解によるものです。

QUERY 1

は10K行が含まtbl_sale(e)の間でインデックスの使用を許可していない設計が不十分な副問合せを使用します。ここ

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 

は、クエリのEXPLAIN 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 

は、クエリのEXPLAINです:

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 

は、どのように私は私にQUERY 1のように、私は必要なだけの行を与えるためにQUERY 2(より効率的な)を変更することができますと連携?クエリ2は、すべて左を使用している間QUERY 1で マイク

+0

最初のクエリがどのようにしてあなたの望みを与えるのかはわかりません。左ジョインは左外ジョインではありません(おそらくMySQLにあり、SQLには準拠していません)。また、NULLは一意の値ではありません。私は便利なMySQLを持っていませんが、これをPostgreSQLに落としても、あなたが記述した結果は得られません。私の答えは... – PlexQ

答えて

0

あなたの副選択は、暗黙のインナージョイン使用している

おかげで、明示的に参加する参加します。

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` 

あなたはまた、服用検討するかもしれない:したがって、私は数行にLEFTのを取る(マークとして)、これは物事をどのように改善するか見たいクエリ2のデータを除外するために何の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とe.flyer_id = d.flyer_id)を持つすべてのアイテムの行を与えますが、(b)フィールドを含むtbl_watch(a)の各アイテムをtbl_sale(e)には存在しません。だから私は終わるだろう:id、sv、description、NULL、NULL、NULL。私は、項目ごとにNULLを持つ1行を許可したいだけでした。私はこれを達成する方法がわかりません。 – ridgeback

+0

明確にするために、私は各アイテムが3つのシナリオのうちの1つに入ると予想します:1 - 単価のアイテム。 2 - 複数の価格を持つ商品。 3 - 価格のないアイテム。 3が発生した場合でも、項目id、sv、およびdescriptionとともに行が返されます。 – 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のバージョンが最適化されます。

関連する問題