web-dev-qa-db-ja.com

2つのMysql VIEWの結合には非常に長い時間がかかります

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、indexesmovieImdbId、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

すべてのアイデアが高く評価され、

3
monamona

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);

私は何百回もそれを経験したので、結合中に全表スキャンが行われることをほぼ保証できます。

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フルテキスト検索は行を返しません

試してみる !!!

1
RolandoMySQLDBA

私はこの問題の専門家ではありませんが、フルテキスト検索とインデックスを調べることをお勧めします。私が理解していることから、文字列の一部を検索する場合、LIKEステートメントよりもはるかに効率的です。 これはドキュメントです 機能について。

0
Neghtasro