PHPユーザーが俳優/女優名を挿入できるフォームにテキストボックス(jQueryオートコンプリートを使用)があり、[検索]ボタンをクリックすると新しいウィンドウが開きます)その俳優/女優の映画のリストを表示します。
これは、俳優名で映画を取得するクエリです。
$query = $conn->prepare("SELECT DISTINCT c.movieName, c.castName, c.movieImdbId, f.year, f.posterLink FROM cast_movie as c JOIN film_info as f ON c.ImdbId = f.ImdbId WHERE c.castName LIKE :q");
$query->execute(array(':q' => '%' . $searchText . '%' ));
私の質問:
上記のクエリは、ユーザーがオートコンプリートリストから名前を選択した場合に正常に機能します。ただし、ユーザーが任意の名前を入力できるようにしたいと思います(たとえオートコンプリートリストから見つけられなかったとしても)。たとえば、ユーザーがテキストボックスに「tom」と入力して「検索」ボタンをクリックすると、名前に「tom」が含まれるすべての俳優によるすべての映画のリストが表示されます。
この目的のために、私はLIKE :q
および':q' => '%' . $searchText . '%'
上記のクエリでは、クエリは終了しません!!(cast_movieは非常に大きい(300万行の)VIEWであり、このビューを他のテーブルと結合するのに非常に長い時間がかかるため(実際には10分でまだ完了していません)。
これを修正する方法があるかどうか誰かが親切に私に知らせてもらえますか? (非常に大きなテーブルを結合するためにインデックスを使用できると読みましたが、viewであるため、cast_movieのインデックスを定義することは不可能だと思います。)
テーブルとビューの詳細:
cast_movieは、「movie_roleNames」と「movies」を結合して作成したビューです。
movie_roleNamesは、2つのテーブル「Cast」と「nameRoles」を結合することによって作成されるビューでもあります。
film_infoは、2つのテーブル「movies」と「movies_info」を結合することによって作成されるビューでもあります。
上記のテーブルの構造:
テーブル"movies":Id、movieName、ImdbId(unique Id of movies)、Rate、numVotes、year(- インデックス:ImdbId、movieName、year)
テーブル"キャスト":castName、castImdbID(キャストの一意のID)(インデックス:castName、castImdbID)
テーブル"nameRoles":Id、castImdbId、movieImdbId、role_Id、(indexes:movieImdbId、castImdbId)
VIEW "movie_roleNames":Id、castName、castImdbId、movieImdbIdそして、結合ステートメントはSELECT n.Id, c.castName, n.castImdbId, n.movieImdbId FROM nameRoles as n join Cast as c ON n.castImdbId = c.castImdbID
VIEW "cast_movie":Id、castName、castImdbId、movieImdbId、movieNameそして、結合ステートメントはSELECT m.Id, r.castName, r.castImdbId, r.movieImdbId, m.movieName FROM movie_roleNames AS r JOIN movies AS m ON r.movieImdbId = m.ImdbId
すべてのアイデアが高く評価され、
FULLTEXTインデックスの使用は、細心の注意を払って処理する必要があります。どうして ? FULLTEXTインデックス検索は機能しますが、MySQLクエリオプティマイザーは、クエリを適切に表現しない場合、全テーブルスキャンを提案する傾向があります。
クエリを取り、「tom」を探しましょう
SELECT DISTINCT c.movieName, c.castName, c.movieImdbId, f.year, f.posterLink
FROM cast_movie as c JOIN film_info as f ON c.ImdbId = f.ImdbId
WHERE c.castName LIKE '%tom%';
これはすべてに参加します。次に、結果の一時テーブルをスキャンし、tom
を含むすべての行を返します。
FULLTEXTメソッドを実装しましょう
SELECT DISTINCT c.movieName, c.castName, c.movieImdbId, f.year, f.posterLink
FROM cast_movie as c JOIN film_info as f ON c.ImdbId = f.ImdbId
WHERE MATCH(c.castName) AGAINST ('+tom' IN BOOLEAN MODE);
私は何百回もそれを経験したので、結合中に全表スキャンが行われることをほぼ保証できます。
Oct 25, 2011
: 'word number'条件付きのブールモードでは、FULLTEXTインデックスは無視されますJan 26, 2012
: Mysql全文検索my.cnf最適化May 07, 2012
: MySQL EXPLAINはFULLTEXTの「インデックスの使用」を表示しませんJul 18, 2012
: 全文検索でLIKEよりも少ない行が返される理由webmasterworld.comと題するMySQL Match Against +非常に高価な結合。これをどのように最適化すればよいですか?
先ほど述べた情報を基に、クエリを取得してリファクタリングしてFULLTEXTを処理し、適切な結合を行います。
SELECT DISTINCT c.movieName, c.castName, c.movieImdbId, f.year, f.posterLink
FROM cast_movie as c JOIN film_info as f ON c.ImdbId = f.ImdbId
WHERE MATCH(c.castName) AGAINST ('+tom' IN BOOLEAN MODE);
まず、 MATCH ... AGAINST
演算子をcast_movie
に適用し、ImdbId
およびその他のフィールドを返します
SELECT ImdbId,movieName,castName,movieImdbId FROM cast_movie
WHERE MATCH(c.castName) AGAINST ('+tom' IN BOOLEAN MODE)
結合してfilm_info
からデータを取得するためのキーを提供するサブクエリを作成する
SELECT DISTINCT c.movieName, c.castName, c.movieImdbId, f.year, f.posterLink FROM
(
SELECT ImdbId,movieName,castName,movieImdbId FROM cast_movie
WHERE MATCH(c.castName) AGAINST ('+tom' IN BOOLEAN MODE)
) as c JOIN film_info as f ON c.ImdbId = f.ImdbId;
これはより良い結果を生むはずです。
テーブルでInnoDBストレージエンジンを使用している場合は、必ずInnoDBフルテキストオプションを調整してください。 innodb_
で始まらないフルテキストオプションはMyISAMを対象としています(私の投稿を参照 文字より短い文字列でのMySQLフルテキスト検索は行を返しません )
試してみる !!!
私はこの問題の専門家ではありませんが、フルテキスト検索とインデックスを調べることをお勧めします。私が理解していることから、文字列の一部を検索する場合、LIKEステートメントよりもはるかに効率的です。 これはドキュメントです 機能について。