9

次のクエリは1.6秒で実行されます

SET @num :=0, @current_shop_id := NULL, @current_product_id := NULL;

#this query limits the results of the query within it by row number (so that only 250 products get displayed per store)

SELECT * FROM (

#this query adds row numbers to the query within it

SELECT *, @num := IF( @current_shop_id = shop_id, IF(@current_product_id=product_id,@num,@num+1), 0) AS row_number, @current_shop_id := shop_id AS shop_dummy, @current_product_id := product_id AS product_dummy FROM (

SELECT shop, shops.shop_id AS
shop_id, p1.product_id AS
product_id 
    FROM products p1 LEFT JOIN #this LEFT JOIN gets the favorites count for each product
  (
  SELECT fav3.product_id AS product_id, SUM(CASE 
    WHEN fav3.current = 1 AND fav3.closeted = 1 THEN 1
    WHEN fav3.current = 1 AND fav3.closeted = 0 THEN -1
    ELSE 0
    END) AS favorites_count
    FROM favorites fav3
GROUP BY fav3.product_id 

  ) AS fav4 ON p1.product_id=fav4.product_id
    INNER JOIN sex ON sex.product_id=p1.product_id AND
    sex.sex=0 AND
    sex.date >= SUBDATE(NOW(),INTERVAL 1 DAY) 
    INNER JOIN shops ON shops.shop_id = p1.shop_id
    ORDER BY shop, sex.DATE, product_id
    ) AS testtable

) AS rowed_results WHERE
rowed_results.row_number>=0 AND
rowed_results.row_number<(7)

最後のWHERE句に追加AND shops.shop_id=86すると、クエリは292秒で実行されます。

SET @num :=0, @current_shop_id := NULL, @current_product_id := NULL;

#this query limits the results of the query within it by row number (so that only 250 products get displayed per store)

SELECT * FROM (

#this query adds row numbers to the query within it

SELECT *, @num := IF( @current_shop_id = shop_id, IF(@current_product_id=product_id,@num,@num+1), 0) AS row_number, @current_shop_id := shop_id AS shop_dummy, @current_product_id := product_id AS product_dummy FROM (

SELECT shop, shops.shop_id AS
shop_id, p1.product_id AS
product_id 
    FROM products p1 LEFT JOIN #this LEFT JOIN gets the favorites count for each product
  (
  SELECT fav3.product_id AS product_id, SUM(CASE 
    WHEN fav3.current = 1 AND fav3.closeted = 1 THEN 1
    WHEN fav3.current = 1 AND fav3.closeted = 0 THEN -1
    ELSE 0
    END) AS favorites_count
    FROM favorites fav3
GROUP BY fav3.product_id 

  ) AS fav4 ON p1.product_id=fav4.product_id
    INNER JOIN sex ON sex.product_id=p1.product_id AND
    sex.sex=0 AND
    sex.date >= SUBDATE(NOW(),INTERVAL 1 DAY)
    INNER JOIN shops ON shops.shop_id = p1.shop_id AND
    shops.shop_id=86
    ORDER BY shop, sex.DATE, product_id
    ) AS testtable

) AS rowed_results WHERE
rowed_results.row_number>=0 AND
rowed_results.row_number<(7)

AND shops.shop_id=86ショップテーブルを制限すると、実行時間が短縮されると思いました。代わりに、実行時間は、products.shop_idが指定されたshops.shop_idと等しいproductsテーブルの行数に依存しているように見えます。products.shop_id = 86のproductsテーブルには約34Kの行があり、実行時間は292秒です。products.shop_id = 50の場合、約28K行あり、実行時間は210秒です。products.shop_id = 175の場合、約2K行あり、実行時間は2.8秒です。何が起こっている?

1.6秒のクエリに対して拡張されたEXPLAINは次のとおりです。

id  select_type table   type    possible_keys   key key_len ref rows    filtered    Extra
1   PRIMARY <derived2>  ALL NULL    NULL    NULL    NULL    1203    100.00  Using where
2   DERIVED <derived3>  ALL NULL    NULL    NULL    NULL    1203    100.00  
3   DERIVED sex ALL product_id_2,product_id NULL    NULL    NULL    526846  75.00   Using where; Using temporary; Using filesort
3   DERIVED p1  eq_ref  PRIMARY,shop_id,shop_id_2,product_id,shop_id_3  PRIMARY 4   mydatabase.sex.product_id   1   100.00  
3   DERIVED <derived4>  ALL NULL    NULL    NULL    NULL    14752   100.00  
3   DERIVED shops   eq_ref  PRIMARY PRIMARY 4   mydatabase.p1.shop_id   1   100.00  
4   DERIVED fav3    ALL NULL    NULL    NULL    NULL    15356   100.00  Using temporary; Using filesort

このEXPLAINEXTENDEDの警告を表示する

-----+
| Note | 1003 | select `rowed_results`.`shop` AS `shop`,`rowed_results`.`shop_id` AS `shop_id`,`rowed_results`.`product_id` AS `product_id`,`rowed_results`.`row_number` AS `row_number`,`rowed_results`.`shop_dummy` AS `shop_dummy`,`rowed_results`.`product_dummy` AS `product_dummy` from (select `testtable`.`shop` AS `shop`,`testtable`.`shop_id` AS `shop_id`,`testtable`.`product_id` AS `product_id`,(@num:=if(((@current_shop_id) = `testtable`.`shop_id`),if(((@current_product_id) = `testtable`.`product_id`),(@num),((@num) + 1)),0)) AS `row_number`,(@current_shop_id:=`testtable`.`shop_id`) AS `shop_dummy`,(@current_product_id:=`testtable`.`product_id`) AS `product_dummy` from (select `mydatabase`.`shops`.`shop` AS `shop`,`mydatabase`.`shops`.`shop_id` AS `shop_id`,`mydatabase`.`p1`.`product_id` AS `product_id` from `mydatabase`.`products` `p1` left join (select `mydatabase`.`fav3`.`product_id` AS `product_id`,sum((case when ((`mydatabase`.`fav3`.`current` = 1) and (`mydatabase`.`fav3`.`closeted` = 1)) then 1 when ((`mydatabase`.`fav3`.`current` = 1) and (`mydatabase`.`fav3`.`closeted` = 0)) then -(1) else 0 end)) AS `favorites_count` from `mydatabase`.`favorites` `fav3` group by `mydatabase`.`fav3`.`product_id`) `fav4` on(((`mydatabase`.`p1`.`product_id` = `mydatabase`.`sex`.`product_id`) and (`fav4`.`product_id` = `mydatabase`.`sex`.`product_id`))) join `mydatabase`.`sex` join `mydatabase`.`shops` where ((`mydatabase`.`sex`.`sex` = 0) and (`mydatabase`.`p1`.`product_id` = `mydatabase`.`sex`.`product_id`) and (`mydatabase`.`shops`.`shop_id` = `mydatabase`.`p1`.`shop_id`) and (`mydatabase`.`sex`.`date` >= (now() - interval 1 day))) order by `mydatabase`.`shops`.`shop`,`mydatabase`.`sex`.`date`,`mydatabase`.`p1`.`product_id`) `testtable`) `rowed_results` where ((`rowed_results`.`row_number` >= 0) and (`rowed_results`.`row_number` < 7)) |
+------

292秒のクエリに対して拡張されたEXPLAINは次のとおりです。

id  select_type table   type    possible_keys   key key_len ref rows    filtered    Extra
1   PRIMARY <derived2>  ALL NULL    NULL    NULL    NULL    36  100.00  Using where
2   DERIVED <derived3>  ALL NULL    NULL    NULL    NULL    36  100.00  
3   DERIVED shops   const   PRIMARY PRIMARY 4       1   100.00  Using temporary; Using filesort
3   DERIVED p1  ref PRIMARY,shop_id,shop_id_2,product_id,shop_id_3  shop_id 4       11799   100.00  
3   DERIVED <derived4>  ALL NULL    NULL    NULL    NULL    14752   100.00  
3   DERIVED sex eq_ref  product_id_2,product_id product_id_2    5   mydatabase.p1.product_id    1   100.00  Using where
4   DERIVED fav3    ALL NULL    NULL    NULL    NULL    15356   100.00  Using temporary; Using filesort

このEXPLAINEXTENDEDの警告を表示する

----+ 
| Note | 1003 | select `rowed_results`.`shop` AS `shop`,`rowed_results`.`shop_id` AS `shop_id`,`rowed_results`.`product_id` AS `product_id`,`rowed_results`.`row_number` AS `row_number`,`rowed_results`.`shop_dummy` AS `shop_dummy`,`rowed_results`.`product_dummy` AS `product_dummy` from (select `testtable`.`shop` AS `shop`,`testtable`.`shop_id` AS `shop_id`,`testtable`.`product_id` AS `product_id`,(@num:=if(((@current_shop_id) = `testtable`.`shop_id`),if(((@current_product_id) = `testtable`.`product_id`),(@num),((@num) + 1)),0)) AS `row_number`,(@current_shop_id:=`testtable`.`shop_id`) AS `shop_dummy`,(@current_product_id:=`testtable`.`product_id`) AS `product_dummy` from (select 'shop.nordstrom.com' AS `shop`,'86' AS `shop_id`,`mydatabase`.`p1`.`product_id` AS `product_id` from `mydatabase`.`products` `p1` left join (select `mydatabase`.`fav3`.`product_id` AS `product_id`,sum((case when ((`mydatabase`.`fav3`.`current` = 1) and (`mydatabase`.`fav3`.`closeted` = 1)) then 1 when ((`mydatabase`.`fav3`.`current` = 1) and (`mydatabase`.`fav3`.`closeted` = 0)) then -(1) else 0 end)) AS `favorites_count` from `mydatabase`.`favorites` `fav3` group by `mydatabase`.`fav3`.`product_id`) `fav4` on(((`fav4`.`product_id` = `mydatabase`.`p1`.`product_id`) and (`mydatabase`.`sex`.`product_id` = `mydatabase`.`p1`.`product_id`))) join `mydatabase`.`sex` join `mydatabase`.`shops` where ((`mydatabase`.`sex`.`sex` = 0) and (`mydatabase`.`sex`.`product_id` = `mydatabase`.`p1`.`product_id`) and (`mydatabase`.`p1`.`shop_id` = 86) and (`mydatabase`.`sex`.`date` >= (now() - interval 1 day))) order by 'shop.nordstrom.com',`mydatabase`.`sex`.`date`,`mydatabase`.`p1`.`product_id`) `testtable`) `rowed_results` where ((`rowed_results`.`row_number` >= 0) and (`rowed_results`.`row_number` < 7)) | 
+-----

MySQLクライアントバージョン5.1.56を実行しています。ショップテーブルには、shop_idにプライマリインデックスがあります。

Action  Keyname Type    Unique  Packed  Column  Cardinality Collation   Null    Comment
 Edit    Drop   PRIMARY BTREE   Yes No  shop_id 163 A

ショップテーブルを分析しましたが、役に立ちませんでした。

LEFT JOIN実行時間の差を取り除くと、0.28秒に対して0.12秒に低下することに気付きました。

Cezのソリューション、つまり、クエリの1.6秒バージョンを使用rowed_results.shop_dummy=86し、外部クエリに追加して無関係な結果を削除する(以下のように)、1.7秒で実行されます。これは問題を回避しますが、なぜ292秒のクエリが非常に遅いのかという謎が残っています。

SET @num :=0, @current_shop_id := NULL, @current_product_id := NULL;

#this query limits the results of the query within it by row number (so that only 250 products get displayed per store)

SELECT * FROM (

#this query adds row numbers to the query within it

SELECT *, @num := IF( @current_shop_id = shop_id, IF(@current_product_id=product_id,@num,@num+1), 0) AS row_number, @current_shop_id := shop_id AS shop_dummy, @current_product_id := product_id AS product_dummy FROM (

SELECT shop, shops.shop_id AS
shop_id, p1.product_id AS
product_id 
    FROM products p1 LEFT JOIN #this LEFT JOIN gets the favorites count for each product
  (
  SELECT fav3.product_id AS product_id, SUM(CASE 
    WHEN fav3.current = 1 AND fav3.closeted = 1 THEN 1
    WHEN fav3.current = 1 AND fav3.closeted = 0 THEN -1
    ELSE 0
    END) AS favorites_count
    FROM favorites fav3
GROUP BY fav3.product_id 

  ) AS fav4 ON p1.product_id=fav4.product_id
    INNER JOIN sex ON sex.product_id=p1.product_id AND sex.sex=0
    INNER JOIN shops ON shops.shop_id = p1.shop_id
    WHERE sex.date >= SUBDATE(NOW(),INTERVAL 1 DAY) 


    ORDER BY shop, sex.DATE, product_id
    ) AS testtable

) AS rowed_results WHERE
rowed_results.row_number>=0 AND
rowed_results.row_number<(7) AND
rowed_results.shop_dummy=86;
4

3 に答える 3

1

チャットルームの後、実際にクエリに一致するテーブル/列を作成した後、次のクエリを思いつきました。

私は、性別、製品(shop_idの場合)、およびお気に入りのテーブルに関する最も内側のクエリを開始しました。ShopAのProductX=製品ID=1であるが、ShopBの同じProductX=製品ID= 2(例のみ)と説明したため、各製品は常にショップごとに一意であり、重複することはありません。そうは言っても、このクエリでお気に入りの数(ある場合)を使用して製品とshop_idを取得できますが、MAX()を使用している製品ごとにshop_idは変更されないため、product_idだけでグループ化します。あなたはいつも「昨日」の日付と性別(sex = 0 female)で探しているので、SEXテーブルに(date、sex、product_id)のインデックスを付けます...毎に1000個のアイテムを追加しているわけではないと思います日...製品には明らかにproduct_id(主キー)のインデックスがあり、お気に入りにはproduct_idのインデックスがあるはずです。

その結果(別名「sxFav」)から、その「Product_ID」によって性別と商品のテーブルに直接結合して、ショップの名前、商品が追加された日付、商品の説明など、必要な追加情報を取得できます。 。この結果は、商品の販売元であるshop_id、日付、最後に商品IDで並べ替えられます(ただし、内部クエリで説明列を取得し、それを並べ替えとして使用することを検討してください)。これにより、エイリアス「PreQuery」が生成されます。

ショップごとの注文がすべて適切であるため、@ MySQLVariable参照を追加して、最初に試みた方法と同様の行番号を各商品に割り当てることができます。ただし、ショップIDが変更された場合にのみ1にリセットしてください。

SELECT 
      PreQuery.*,
      @num := IF( @current_shop_id = PreQuery.shop_id, @num +1, 1 ) AS RowPerShop, 
      @current_shop_id := PreQuery.shop_id AS shop_dummy 
   from 
      ( SELECT 
              sxFav.product_id, 
              sxFav.shop_id, 
              sxFav.Favorites_Count
           from 
              ( SELECT 
                      sex.product_id,
                      MAX( p.shop_id ) shop_id,
                      SUM( CASE WHEN F.current = 1 AND F.closeted = 1 THEN 1 
                                WHEN F.current = 1 AND F.closeted = 0 THEN -1 
                                ELSE 0 END ) AS favorites_count 
                   from 
                      sex
                         JOIN products p
                            ON sex.Product_ID = p.Product_ID
                         LEFT JOIN Favorites F 
                            ON sex.product_id = F.product_ID 
                   where 
                          sex.date >= subdate( now(), interval 1 day) 
                      and sex.sex = 0 
                   group by 
                      sex.product_id ) sxFav 

              JOIN sex 
                 ON sxFav.Product_ID = sex.Product_ID

              JOIN products p
                 ON sxFav.Product_ID = p.Product_ID
      order by 
         sxFav.shop_id, 
         sex.date, 
         sxFav.product_id ) PreQuery,

     ( select @num :=0, 
              @current_shop_id := 0 ) as SQLVars 

ここで、特定の「ページング」情報(ショップごとに7つのエントリなど)を探している場合は、上記のクエリ全体を次のようにラップします...

select * from ( entire query above ) where RowPerShop between 1 and 7

(または必要に応じて8〜14、15〜21など)または

RowPerShop between RowsPerPage*PageYouAreShowing and RowsPerPage*(PageYouAreShowing +1)
于 2012-11-16T01:47:42.587 に答える
1

議論から判断すると、クエリプランナーは、下位​​レベルでショップを指定するときにパフォーマンスが低下しています。

外側のクエリに追加rowed_results.shop_dummy=86して、探している結果を取得します。

于 2012-11-16T00:53:45.023 に答える
1

shop.shop_id=86をショップのJOIN条件に移動する必要があります。それをJOINの外に置く理由はありません。最初に、MySQL JOINを実行し、次にフィルタリングするリスクがあります。JOINは、特に他のテーブルを参照していない場合、WHERE句と同じジョブを実行できます。

....
INNER JOIN shops ON shops.shop_id = p1.shop_id AND shops.shop_id=86
....

セックス参加と同じこと:

...
INNER JOIN shops ON shops.shop_id = p1.shop_id
AND sex.date >= SUBDATE(NOW(),INTERVAL 1 DAY)
...

派生テーブルは優れていますが、インデックスはありません。それらは一般的にRAMにあるので、通常これは問題ではありません。ただし、インデックスを使用しないフィルタリングと並べ替えの間では、状況が悪化する可能性があります。

はるかに時間がかかる2番目のクエリでは、テーブルの処理順序が変わることに注意してください。ショップテーブルは低速クエリの一番上にあり、p1テーブルは高速クエリの1行ではなく11799行を取得します。また、主キーを使用しなくなります。それはおそらくあなたの問題がどこにあるかです。

3   DERIVED p1  eq_ref  PRIMARY,shop_id,shop_id_2,product_id,shop_id_3  PRIMARY 4   mydatabase.sex.product_id   1   100.00  

3   DERIVED p1  ref PRIMARY,shop_id,shop_id_2,product_id,shop_id_3  shop_id 4       11799   100.00  
于 2012-11-16T02:55:27.493 に答える