2009-03-20 8 views
2

このMySQLのクエリはMySQLの構文と「OR」パフォーマンス

SELECT o.id 
FROM descriptions_programs d, titles_programs t, programs o 
WHERE (d.object_id=o.id 
     AND MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) 
     AND d.current=1) 
AND (t.object_id=o.id 
     AND MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) 
     AND t.current=1) 

だけで正常に動作しかし、私は1 AND ORとを交換した場合、クエリは非常に長い時間を実行します。 (私はそれを殺す必要があります):

SELECT o.id 
FROM descriptions_programs d, titles_programs t, programs o 
WHERE (d.object_id=o.id 
     AND MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) 
     AND d.current=1) 
OR (t.object_id=o.id 
     AND MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) 
     AND t.current=1) 

これはなぜですか? +中国のシンプルさにハングアップしないでください。私はデバッグのためにこれを単純化しました。また、MATCH AGAINSTテストのうちの1つだけを実行すると、正常に動作するので、どちらも問題ありません。私は誤ってORを使用して巨大な結合を引き起こしているという感覚を得るが、私はそれを得ていない。私は以前、2つの副選択のUNIONでn INテストを使用していましたが、これもうまくいくはずです。右?

更新日: per bobinceさんのリクエストそれは超低速ではありませんが、〜500msで、それはUNIONをdiscussed hereとして使用するほど速くはありません。

mysql> explain SELECT o.id 
    -> FROM programs o 
    -> JOIN titles_programs t ON t.object_id=o.id 
    -> JOIN descriptions_programs d ON d.object_id=o.id 
    -> WHERE MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) AND d.current=1 
    -> OR MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) AND t.current=1 
    -> ; 
+----+-------------+-------+-------+ 

----------------+----------------+---------+----------------------+--------+-------------+ 
| id | select_type | table | type | possible_keys | key   | key_len | ref     | rows | Extra  | 
+----+-------------+-------+-------+----------------+----------------+---------+----------------------+--------+-------------+ 
| 1 | SIMPLE  | o  | index | PRIMARY  | PRIMARY  | 4  | NULL     | 148666 | Using index | 
| 1 | SIMPLE  | d  | ref | object_current | object_current | 4  | haystack.o.id  |  1 |    | 
| 1 | SIMPLE  | t  | ref | object_current | object_current | 4  | haystack.d.object_id |  1 | Using where | 
+----+-------------+-------+-------+----------------+----------------+---------+----------------------+--------+-------------+ 

答えて

1

あなたの問題はodt間の結合は、すべての場合に発生する必要があるということです。すなわち、あなたが必要です:

SELECT o.id 
FROM descriptions_programs d, titles_programs t, programs o 
WHERE d.object_id=o.id AND t.object_id=o.id AND 
(
     MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) 
     AND d.current=1 
) OR ( MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) 
     AND t.current=1 
) 

なぜですか?あなたの最初のクエリでは、それらの括弧を無視することができます - すべてがANDであり、テーブルが正常に結合します。 2番目のクエリでは、それは当てはまりません。

データベースが実際に何をしているのかを考えてみましょう。「すべての行を」とみなし、「すべての行がd」と交差するので、t*d行です。通常、あなたはそれを有効な行の線形リストに制限するために(私が行ったように)結合を使います。

しかし、あなたのORクエリで、あなたはまた、他のテーブルにすべての行を選択していると一致する1つのテーブルの行ごとにので、o代わりの両方oを合わせると一致するように、いずれか行を許可します。

+0

まだ違いはありません。私はそれを殺す前に> 1分間実行されます。 ORをANDに変更し、ミリ秒単位で実行します。 2つのMATCH AGAINSTフレーズのいずれかを削除する場合も同じです。 –

+0

't'テーブルだけを実行してみてください。 "FROM"と "WHERE"節の両方から 'd'を取り除く。その後、実行時間は何ですか?答えが "それは永遠にかかる"場合、それは "OR"の問題ではありません、それは単にテーブルスキャンが本当に遅いです。 –

+0

「OR」を「AND」に変更するか、またはt * dの動作になるかに注意してください。また、あいまいさの場合にはカッコを入れる必要があります:MySQLのコンベンションが分かりませんが、 "foo and bar or car"と言うと、 "foo and bar"や "car"を意味する可能性があります。 –

2

ジェイソンの答えはスポットです。さらに、私はそこに混乱緩和WHERE句オフロードを取るために、より近代的なANSI結合構文を使用しようと思います:

SELECT o.id 
FROM programs o 
JOIN titles_programs t ON t.object_id=o.id 
JOIN descriptions_programs d ON d.object_id=o.id 
WHERE MATCH (d.text) AGAINST ('+china' IN BOOLEAN MODE) AND d.current=1 
OR MATCH (t.text) AGAINST ('+china' IN BOOLEAN MODE) AND t.current=1 

これは停止しますが不注意なクロス参加組み合わせ爆発の原因となります。私はそれがデータベースが本当に巨大でなければ、合理的な時間に動作すると期待しています。

もしそうでない場合は、上記のEXPLAIN SELECTの結果を投稿できますか?おそらく、全文索引の一方または両方が使用されていない可能性があります。私は確かに、クエリオプティマイザが、インデックスにまっすぐ行くのではなく、最初のフルテキストクエリと一致しなかった行を「埋めてみる」のようなことをして、2番目のフルテキストインデックスを使用しないと想像することができます。

通常、2つの列を組み合わせてインデックスをフルテキストにする場合は、両方の列に1つのインデックスを作成します。いずれにせよ、これははるかに速いでしょう。しかし、タイトルと説明を同じテーブルに入れなければならないことを意味します。これは難しいことではないかもしれません。フルテキストはMyISAMテーブルでしか動作しないので(通常はMyISAMテーブルのカノニカルデータを必要としません)、適切に正規化されたInnoDBテーブルにデータの最終コピーを保存し、剥奪されて茎にされた探索餌のみを含む。

これはうまくいきません...私はあなたが言及したUNIONに戻り、重複IDを削除するためのアプリケーションレベルのフィルタに戻ったと思います。