2012-01-17 8 views
1

私は、WordPressのWP_Queryクラスで生成された次のクエリを書くより良い方法を見つけようとしています。今は非常にです。MySQL。ワードプレス。 IN文を使用するとクエリが遅くなる

SELECT SQL_CALC_FOUND_ROWS wp_posts.* 
FROM wp_posts 
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id) 
INNER JOIN wp_postmeta AS mt4 ON (wp_posts.ID = mt4.post_id) 
INNER JOIN wp_postmeta AS mt5 ON (wp_posts.ID = mt5.post_id) 
INNER JOIN wp_postmeta AS mt6 ON (wp_posts.ID = mt6.post_id) 
INNER JOIN wp_postmeta AS mt7 ON (wp_posts.ID = mt7.post_id) 
INNER JOIN wp_postmeta AS mt8 ON (wp_posts.ID = mt8.post_id) 

WHERE 1=1 AND wp_posts.post_type = 'gemstone' 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' OR wp_posts.post_status = 'private') 
AND (wp_postmeta.meta_key = 'gemstone_active_price' 
    AND (mt1.meta_key = 'gemstone_status' AND CAST(mt1.meta_value AS CHAR) = 'Available') 
    AND (mt2.meta_key = 'gemstone_length' AND CAST(mt2.meta_value AS DECIMAL(10,2)) BETWEEN '0' AND '9') 
    AND (mt3.meta_key = 'gemstone_width' AND CAST(mt3.meta_value AS DECIMAL(10,2)) BETWEEN '0' AND '9') 
    AND (mt4.meta_key = 'gemstone_depth' AND CAST(mt4.meta_value AS DECIMAL(10,2)) BETWEEN '0' AND '7') 
    AND (mt5.meta_key = 'gemstone_color' AND CAST(mt5.meta_value AS CHAR) IN ('L','K','J','I','H','G','F','E','D')) 
    AND (mt6.meta_key = 'gemstone_clarity' AND CAST(mt6.meta_value AS CHAR) IN ('I3','I2','I1','SI2','SI1','VS2','VVS2','VVS1','IF','FL')) 
    AND (mt7.meta_key = 'gemstone_weight' AND CAST(mt7.meta_value AS DECIMAL(10,2)) BETWEEN '0.67' AND '1.85') 
    AND (mt8.meta_key = 'gemstone_active_price' AND CAST(mt8.meta_value AS DECIMAL(10,2)) BETWEEN '960' AND '2300') 
) 

GROUP BY wp_posts.ID 
ORDER BY wp_postmeta.meta_value+0 ASC 
LIMIT 0, 20 

私はそれは大きな混乱のように見えるけど、私は、WHERE句(MT5とMT6上記)で2つのIN文を持っていないとき、全体の事は非常に迅速に実行されます。問題は、INステートメントの使用を避けるために、クエリを書く別の方法を理解するのにSQLをよく知っていないことです。何か案は?

UPDATE:ここ はそれが誰を助ける場合には、このクエリのEXPLAIN出力されます。誰か他の考えがあれば、私は何でもできる。これで私は完全に困惑しました。

id select_type  table   type possible_keys    key   key_len  ref       rows Extra 
1 SIMPLE   wp_postmeta  ref  post_id,meta_key   meta_key 768   const      2  Using where; Using temporary; Using filesort 
1 SIMPLE   mt1    ref  post_id,meta_key   post_id  8   db.wp_postmeta.post_id  2  Using where 
1 SIMPLE   mt2    ref  post_id,meta_key   post_id  8   db.mt1.post_id    2  Using where 
1 SIMPLE   mt3    ref  post_id,meta_key   post_id  8   db.wp_postmeta.post_id  2  Using where 
1 SIMPLE   mt4    ref  post_id,meta_key   post_id  8   db.mt2.post_id    2  Using where 
1 SIMPLE   mt5    ref  post_id,meta_key   post_id  8   db.wp_postmeta.post_id  2  Using where 
1 SIMPLE   mt6    ref  post_id,meta_key   post_id  8   db.wp_postmeta.post_id  2  Using where 
1 SIMPLE   mt7    ref  post_id,meta_key   post_id  8   db.mt3.post_id    2  Using where 
1 SIMPLE   mt8    ref  post_id,meta_key   post_id  8   db.wp_postmeta.post_id  2  Using where 
1 SIMPLE   wp_posts  eq_ref PRIMARY,type_status_date PRIMARY  8   db.wp_postmeta.post_id  1  Using where 

UPDATE 2:いくつかのより多くの実験をした後 、私はそれがこのクエリを遅くしているだけでIN()文ではないことを実現しています。複数のIN()と3つ以上のBETWEEN...AND...ステートメントの組み合わせがパフォーマンスに劇的な影響を与えるようです。例えば

、私は最後の2つのAND条項(彼らと4.9s対)をドロップする場合、クエリを約0.04sで実行される、または私はIN()文で2つのAND条項を削除した場合、それは0.04sで実行されます。これは、2つのクエリソリューションが最適かもしれないと思うようになりますが、WordPressのWP_Query APIを使って実装する方法がわかりません。もし私がそうしたとしても、それはただ1つのクエリを実行してからフィルタリングするよりも早いでしょう結果はPHPを介して。

私はいくつかの場所でフィルタリングをデータベースに残さなければならないということを読んだので、PHPでフィルタリングするのは嫌です。ちなみに、違いがあれば、処理能力の高いコンピュータ(Intel i7、12 GB RAMなど)にあるlocalhost WAMPサーバーのWordPress 3.3.1インストールでこれらのクエリを実行しています。

UPDATE 3: 私はあきらめとPHPを経由してそれらのためのクエリとフィルタからすべてのIN()条項の削除について考えていたが、それはいくつかの深刻なドローバックを持っています。非効率的でコードの匂いがするだけでなく、私のページネーションを正しく制御することもできません。すべてがデータベースでフィルタリングされている場合は、LIMIT句を使用してページネーションを処理できます。私がPHPでフィルターをかけると、どのオフセットに対してどれだけ多くの結果が返されるか分かりません。だから、すべてのフィルタリングは実際にデータベースで行う必要がありますが、問題はどうなるのでしょうか。誰にも私のために何か提案がありますか?追加情報は誰にも役立つだろうか?

UPDATE 4:これを解決するための私の検索で 、私はWordPressのコアのTracシステム(http://core.trac.wordpress.org/ticket/20134)での問題として、それを投稿しました。そこにある開発者の1人は、私がINを私のメタ・クエリーに使用しているもののメタデータの代わりにタクソノミーを使用しようと提案しました。私はそのアドバイスを受けて、パフォーマンスの向上を見ましたが、残念ながら、それほど十分ではありませんでした。古いクエリは実行に4秒以上かかり、分類法を使用すると1秒以上になりました。しかし、私は実際には4 IN型節(元の2ではなく)が必要であることに気付きました。 2つの追加タクソノミー句を使用すると、クエリの実行に18秒以上かかります。だから、私は正方形に戻ってきた。私が持っていた考え(おそらく妄想的なことです)は、基準に合致する投稿が少なくて済むので、これは非常に遅くなる可能性があります。テスト目的のために、私はポストタイプ'gemstone'を持つ3つのポストをデータベースに持っています。それはそれと関係がありますか?誰もが興味を持っている場合は

は、私の新しいSQLは次のようになります。

SELECT SQL_CALC_FOUND_ROWS wp_posts.* 
FROM wp_posts 
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) 
INNER JOIN wp_term_relationships AS tt1 ON (wp_posts.ID = tt1.object_id) 
INNER JOIN wp_term_relationships AS tt2 ON (wp_posts.ID = tt2.object_id) 
INNER JOIN wp_term_relationships AS tt3 ON (wp_posts.ID = tt3.object_id)  
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id) 
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id) 
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) 
INNER JOIN wp_postmeta AS mt3 ON (wp_posts.ID = mt3.post_id) 
INNER JOIN wp_postmeta AS mt4 ON (wp_posts.ID = mt4.post_id) 
INNER JOIN wp_postmeta AS mt5 ON (wp_posts.ID = mt5.post_id) 
INNER JOIN wp_postmeta AS mt6 ON (wp_posts.ID = mt6.post_id) 

WHERE 1=1 
AND (wp_term_relationships.term_taxonomy_id IN (71,72,73,74) 
    AND tt1.term_taxonomy_id IN (89,90,91,92,93,95,96,97) 
    AND tt2.term_taxonomy_id IN (56,50,104,53) 
    AND tt3.term_taxonomy_id IN (59,60,62) 
) 
AND wp_posts.post_type = 'gemstone' 
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'future' OR wp_posts.post_status = 'draft' OR wp_posts.post_status = 'pending' OR wp_posts.post_status = 'private') 
AND (wp_postmeta.meta_key = 'gemstone_weight' 
    AND (mt1.meta_key = 'gemstone_status' AND CAST(mt1.meta_value AS CHAR) = 'Available') 
    AND (mt2.meta_key = 'gemstone_length' AND CAST(mt2.meta_value AS DECIMAL(8,2)) BETWEEN '0' AND '9') 
    AND (mt3.meta_key = 'gemstone_width' AND CAST(mt3.meta_value AS DECIMAL(8,2)) BETWEEN '0' AND '9') 
    AND (mt4.meta_key = 'gemstone_depth' AND CAST(mt4.meta_value AS DECIMAL(8,2)) BETWEEN '0' AND '7') 
    AND (mt5.meta_key = 'gemstone_weight' AND CAST(mt5.meta_value AS DECIMAL(8,2)) BETWEEN '0.81' AND '1.81') 
    AND (mt6.meta_key = 'gemstone_active_price' AND CAST(mt6.meta_value AS DECIMAL(8,2)) BETWEEN '1083.9' AND '2078.26') 
) 

GROUP BY wp_posts.ID 
ORDER BY wp_postmeta.meta_value+0 ASC 
LIMIT 0, 20 

と新しいEXPLAIN出力は次のとおりです。

id select_type  table     type possible_keys    key   key_len  ref      rows Extra 
1 SIMPLE   wp_postmeta    ref  post_id,meta_key   meta_key 768   const     3  Using where; Using temporary; Using filesort 
1 SIMPLE   tt3      ref  PRIMARY,term_taxonomy_id PRIMARY  8   db.wp_postmeta.post_id 1  Using where; Using index 
1 SIMPLE   tt2      ref  PRIMARY,term_taxonomy_id PRIMARY  8   db.wp_postmeta.post_id 1  Using where; Using index 
1 SIMPLE   wp_term_relationships ref  PRIMARY,term_taxonomy_id PRIMARY  8   db.tt2.object_id  1  Using where; Using index 
1 SIMPLE   wp_posts    eq_ref PRIMARY,type_status_date PRIMARY  8   db.wp_postmeta.post_id 1  Using where 
1 SIMPLE   tt1      ref  PRIMARY,term_taxonomy_id PRIMARY  8   db.wp_posts.ID   1  Using where; Using index 
1 SIMPLE   mt5      ref  post_id,meta_key   post_id  8   db.wp_posts.ID   2  Using where 
1 SIMPLE   mt6      ref  post_id,meta_key   post_id  8   db.wp_posts.ID   2  Using where 
1 SIMPLE   mt1      ref  post_id,meta_key   post_id  8   db.mt5.post_id   2  Using where 
1 SIMPLE   mt2      ref  post_id,meta_key   post_id  8   db.mt1.post_id   2  Using where 
1 SIMPLE   mt3      ref  post_id,meta_key   post_id  8   db.tt2.object_id  2  Using where 
1 SIMPLE   mt4      ref  post_id,meta_key   post_id  8   db.tt3.object_id  2  Using where 

UPDATE 5: のでコメントのを、私は最近optimizing this queryで別の刺し傷を取ったが、私はSQLがかなりセットアップされているという結論が残っていた。しかし、いくつかの選択肢をテストする際に、私は不思議なことに、クエリがより速く実行されることを発見しました。 MySQLサーバーを更新していないので、WordPressがパフォーマンスを向上させるためにデータベース構造を更新したということだけがわかります。アップデート4に表示されているのとまったく同じクエリは、現在約2.4秒かかります。私の意見ではまだまだ長すぎます(私はまだ私の答えに示されているようにSTRAIGHT_JOINを使用しています)が、改善に驚いています。ここに新しいEXPLAIN出力があります。それは私とほとんど同じですが、私はそれをどのように解釈するのか本当に分かりません。

+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+ 
| id | select_type |   table   | type |  possible_keys  | key  | key_len |    ref     | rows |     Extra      | 
+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+ 
| 1 | SIMPLE  | wp_postmeta   | ref  | post_id,meta_key   | meta_key |  768 | const        | 5 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | wp_term_relationships | ref  | PRIMARY,term_taxonomy_id | PRIMARY |  8 | db.wp_postmeta.post_id    | 1 | Using where; Using index      | 
| 1 | SIMPLE  | tt2     | ref  | PRIMARY,term_taxonomy_id | PRIMARY |  8 | db.wp_term_relationships.object_id | 1 | Using where; Using index      | 
| 1 | SIMPLE  | tt3     | ref  | PRIMARY,term_taxonomy_id | PRIMARY |  8 | db.wp_term_relationships.object_id | 1 | Using where; Using index      | 
| 1 | SIMPLE  | wp_posts    | eq_ref | PRIMARY,type_status_date | PRIMARY |  8 | db.wp_postmeta.post_id    | 1 | Using where         | 
| 1 | SIMPLE  | tt1     | ref  | PRIMARY,term_taxonomy_id | PRIMARY |  8 | db.wp_posts.ID      | 1 | Using where; Using index      | 
| 1 | SIMPLE  | mt3     | ref  | post_id,meta_key   | post_id |  8 | db.tt2.object_id     | 3 | Using where         | 
| 1 | SIMPLE  | mt4     | ref  | post_id,meta_key   | post_id |  8 | db.tt3.object_id     | 3 | Using where         | 
| 1 | SIMPLE  | mt5     | ref  | post_id,meta_key   | post_id |  8 | db.wp_posts.ID      | 3 | Using where         | 
| 1 | SIMPLE  | mt6     | ref  | post_id,meta_key   | post_id |  8 | db.wp_posts.ID      | 3 | Using where         | 
| 1 | SIMPLE  | mt1     | ref  | post_id,meta_key   | post_id |  8 | db.mt5.post_id      | 3 | Using where         | 
| 1 | SIMPLE  | mt2     | ref  | post_id,meta_key   | post_id |  8 | db.mt3.post_id      | 3 | Using where         | 
+-----+--------------+------------------------+---------+---------------------------+-----------+----------+-------------------------------------+-------+----------------------------------------------+ 
+0

アップデートとして。 phpMyAdminでプロファイリングをオンにしてこのクエリを実行すると、「統計」が経過した時間の99%を占めます。この文脈で「統計」が何を意味するか知っていますか? –

+0

ここで説明したように、追加のINNER JOINを削除してみてください:http://stackoverflow.com/a/15398104/212076 – KalenGi

答えて

0

"meta_value"フィールドにインデックスがあるかどうかを確認できますか?これにより、スピードが飛躍的に向上します。

私はそれについてはわかりませんが、おそらくCAST()関数はいくつかの減速を引き起こしますか?彼らは必要ですか?

+0

"meta_value"にはインデックスがありません。 WordPressの開発者はそれが必要だとは思わなかったと思う。 CAST()関数は、小数精度を定義しており、さらに重要なことに、IN()ステートメントが存在しないときに何かを遅くするように見えないので、ほとんどのWHERE句に必要です。私は確認するためにいくつかのテストを行います。 –

+1

'meta_value'のインデックス自体は解決策ではありません。なぜなら、その結果が直接比較で使われることは決してないからです。それは常に関数に渡されます。 – newtover

+0

私は 'meta_value'にインデックスを追加した後にクエリを再テストしましたが、インデックスが効果がないことを確認できます。また、徹底させるために、 'CAST()'関数を使わずに試してみましたが、残念ながらそれは効果がありませんでした。 –

2

私が今すぐ見つけた "解決策"はかなり醜いですが、説明できない理由のために、それは機能します。 STRAIGHT_JOINオプティマイザヒントを追加すると、実行時間が18秒以上から約0.0022秒に短縮されました。常識とこの質問(When to use STRAIGHT_JOIN with MySQL)に基づいて、この解決法は悪い考えのように思えますが、私が試した唯一のことは成功しました。だから、今は少なくとも、私はそれに固執しています。なぜ私がこれをしてはならないのか、あるいは私が代わりに何をやってみるべきなのかについて誰かが考えているなら、私は彼らのことを聞きたいです。誰でも好奇心旺盛であれば

が、私はそうのようなWordPressのフィルターとして、それを実装:

function use_straight_join($distinct_clause) { 

    $distinct_clause = ($use_straight_join) ? 'STRAIGHT_JOIN' . $distinct_clause : $distinct_clause; 

    return $distinct_clause; 
} 
add_filter('posts_distinct', 'use_straight_join'); 

そしてSTRAIGHT_JOINを使用した場合、完全を期すために、ここではクエリのEXPLAIN出力されます。再び、私はうんざりです。古いクエリではrangeよりも速いと理解しているrefeq_refしか使用していませんでしたが、何らかの理由でこれよりも速くなりました。

+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+ 
| id | select_type |   table   | type |  possible_keys  |  key   | key_len |  ref  | rows |     Extra      | 
+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+ 
| 1 | SIMPLE  | wp_posts    | range | PRIMARY,type_status_date | type_status_date |  124 | NULL   | 6 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | wp_postmeta   | ref | post_id,meta_key   | post_id   |  8 | db.wp_posts.ID | 2 | Using where         | 
| 1 | SIMPLE  | mt1     | ref | post_id,meta_key   | post_id   |  8 | db.wp_posts.ID | 2 | Using where         | 
| 1 | SIMPLE  | mt2     | ref | post_id,meta_key   | post_id   |  8 | db.wp_posts.ID | 2 | Using where         | 
| 1 | SIMPLE  | mt3     | ref | post_id,meta_key   | post_id   |  8 | db.wp_posts.ID | 2 | Using where         | 
| 1 | SIMPLE  | mt4     | ref | post_id,meta_key   | post_id   |  8 | db.wp_posts.ID | 2 | Using where         | 
| 1 | SIMPLE  | mt5     | ref | post_id,meta_key   | post_id   |  8 | db.mt3.post_id | 2 | Using where         | 
| 1 | SIMPLE  | mt6     | ref | post_id,meta_key   | post_id   |  8 | db.wp_posts.ID | 2 | Using where         | 
| 1 | SIMPLE  | wp_term_relationships | ref | PRIMARY,term_taxonomy_id | PRIMARY   |  8 | db.wp_posts.ID | 1 | Using where; Using index      | 
| 1 | SIMPLE  | tt1     | ref | PRIMARY,term_taxonomy_id | PRIMARY   |  8 | db.wp_posts.ID | 1 | Using where; Using index      | 
| 1 | SIMPLE  | tt2     | ref | PRIMARY,term_taxonomy_id | PRIMARY   |  8 | db.mt1.post_id | 1 | Using where; Using index      | 
| 1 | SIMPLE  | tt3     | ref | PRIMARY,term_taxonomy_id | PRIMARY   |  8 | db.wp_posts.ID | 1 | Using where; Using index      | 
+-----+--------------+------------------------+--------+---------------------------+-------------------+----------+-----------------+-------+----------------------------------------------+ 
関連する問題