キーワード検索述語を使用すると、テーブル スキャンが強制されるためLIKE '%pattern%'
、確実にパフォーマンスが低下します。
関係除算クエリを実行する最善の方法、つまり 3 つの条件がすべて一致する映画のみを一致させる方法は、各条件の個々の行を見つけて、それらを JOIN することです。
SELECT f.*, CONCAT_WS(' ', a1.ambienceName, a2.ambienceName, a3.ambienceName) AS ambiences
FROM Films AS f
INNER JOIN Films_Ambiences as fa1 ON f.id = fa1.film_id
INNER JOIN Ambiences AS a1 ON a1.id = fa1.ambience_id
INNER JOIN Films_Ambiences as fa2 ON f.id = fa2.film_id
INNER JOIN Ambiences AS a2 ON a2.id = fa2.ambience_id
INNER JOIN Films_Ambiences as fa3 ON f.id = fa3.film_id
INNER JOIN Ambiences AS a3 ON a3.id = fa3.ambience_id
WHERE (a1.ambienceName, a2.ambienceName, a3.ambienceName) = (?, ?, ?);
検索用語ごとJOIN
に toFilms_Ambiences
とfor を追加する必要があります。Ambiences
にインデックスを作成する必要があります。そうすれば、 ambienceName
3 つの検索すべてがより効率的になります。
ALTER TABLE Ambiences ADD KEY (ambienceName);
最近のプレゼンテーションで、関係分割のさまざまなソリューションを比較しました。
あなたのコメントについて:
基準が見つかった後に残りのアンビエンスも表示するように、このクエリを変更する方法はありますか?
はい。ただし、映画の雰囲気の完全なセットを取得するには、もう一度参加する必要があります。
SELECT f.*, GROUP_CONCAT(a_all.ambienceName) AS ambiences
FROM Films AS f
INNER JOIN Films_Ambiences as fa1 ON f.id = fa1.film_id
INNER JOIN Ambiences AS a1 ON a1.id = fa1.ambience_id
INNER JOIN Films_Ambiences as fa2 ON f.id = fa2.film_id
INNER JOIN Ambiences AS a2 ON a2.id = fa2.ambience_id
INNER JOIN Films_Ambiences as fa3 ON f.id = fa3.film_id
INNER JOIN Ambiences AS a3 ON a3.id = fa3.ambience_id
INNER JOIN Films_Ambiences AS fa_all ON f.id = fa_all.film_id
INNER JOIN Ambiences AS a_all ON a_all.id = fa_all.ambience_id
WHERE (a1.ambienceName, a2.ambienceName, a3.ambienceName) = (?, ?, ?)
GROUP BY f.id;
このクエリを変更して、結果が必要な雰囲気を備えた映画のみになるようにする方法はありますか?
上記のクエリはそれを行う必要があります。
クエリが行うことは、与えられたアンビエンスを含む映画を探すことだと思います (したがって、より多くのアンビエンスを持つ映画も見つけます)。
そうです、検索基準の 3 つの雰囲気すべてに一致しない限り、クエリは映画に一致しません。しかし、フィルムには検索基準以外のアンビエンスが含まれる場合があり、フィルムのすべてのアンビエンス (検索基準にあるものとその他のもの) は として収集されGROUP_CONCAT(a_all.ambienceName)
ます。
この例をテストしました:
mysql> INSERT INTO Ambiences (ambienceName)
VALUES ('funny'), ('scary'), ('1950s'), ('London'), ('bank'), ('crime'), ('stupid');
mysql> INSERT INTO Films (title)
VALUES ('Mary Poppins'), ('Heist'), ('Scary Movie'), ('Godzilla'), ('Signs');
mysql> INSERT INTO Films_Ambiences
VALUES (1,1),(1,2),(1,4),(1,5), (2,1),(2,2),(2,5),(2,6), (3,1),(3,2),(3,7), (4,2),(4,3), (5,2),(5,7);
mysql> SELECT f.*, GROUP_CONCAT(a_all.ambienceName) AS ambiences
FROM Films AS f
INNER JOIN Films_Ambiences as fa1 ON f.id = fa1.film_id
INNER JOIN Ambiences AS a1 ON a1.id = fa1.ambience_id
INNER JOIN Films_Ambiences as fa2 ON f.id = fa2.film_id
INNER JOIN Ambiences AS a2 ON a2.id = fa2.ambience_id
INNER JOIN Films_Ambiences as fa3 ON f.id = fa3.film_id
INNER JOIN Ambiences AS a3 ON a3.id = fa3.ambience_id
INNER JOIN Films_Ambiences AS fa_all ON f.id = fa_all.film_id
INNER JOIN Ambiences AS a_all ON a_all.id = fa_all.ambience_id
WHERE (a1.ambienceName, a2.ambienceName, a3.ambienceName) = ('funny','scary','bank')
GROUP BY f.id;
+----+--------------+-------------------------+
| id | Title | ambiences |
+----+--------------+-------------------------+
| 1 | Mary Poppins | funny,scary,London,bank |
| 2 | Heist | funny,scary,bank,crime |
+----+--------------+-------------------------+
ちなみに、インデックスの使用法を示すEXPLAINは次のとおりです。
+----+-------------+--------+--------+----------------------+--------------+---------+-----------------------------+------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------+--------+----------------------+--------------+---------+-----------------------------+------+-----------------------------------------------------------+
| 1 | SIMPLE | a1 | ref | PRIMARY,ambienceName | ambienceName | 258 | const | 1 | Using where; Using index; Using temporary; Using filesort |
| 1 | SIMPLE | a2 | ref | PRIMARY,ambienceName | ambienceName | 258 | const | 1 | Using where; Using index |
| 1 | SIMPLE | a3 | ref | PRIMARY,ambienceName | ambienceName | 258 | const | 1 | Using where; Using index |
| 1 | SIMPLE | fa1 | ref | PRIMARY,ambience_id | ambience_id | 4 | test.a1.id | 1 | Using index |
| 1 | SIMPLE | f | eq_ref | PRIMARY | PRIMARY | 4 | test.fa1.film_id | 1 | NULL |
| 1 | SIMPLE | fa2 | eq_ref | PRIMARY,ambience_id | PRIMARY | 8 | test.fa1.film_id,test.a2.id | 1 | Using index |
| 1 | SIMPLE | fa3 | eq_ref | PRIMARY,ambience_id | PRIMARY | 8 | test.fa1.film_id,test.a3.id | 1 | Using index |
| 1 | SIMPLE | fa_all | ref | PRIMARY,ambience_id | PRIMARY | 4 | test.fa1.film_id | 1 | Using index |
| 1 | SIMPLE | a_all | eq_ref | PRIMARY | PRIMARY | 4 | test.fa_all.ambience_id | 1 | NULL |
+----+-------------+--------+--------+----------------------+--------------+---------+-----------------------------+------+-----------------------------------------------------------+
私は、怖くて、面白くて、ばかげた映画を持っています。怖いだけの映画を検索すると、とにかく映画1が表示されます。私がそれを望まない場合はどうなりますか?
ああ、わかりました。それがあなたの言いたいことだとはまったく理解できませんでした。この種の問題では珍しい要件です。
解決策は次のとおりです。
mysql> SELECT f.*, GROUP_CONCAT(a_all.ambienceName) AS ambiences
FROM Films AS f
INNER JOIN Films_Ambiences as fa1 ON f.id = fa1.film_id
INNER JOIN Ambiences AS a1 ON a1.id = fa1.ambience_id
INNER JOIN Films_Ambiences as fa2 ON f.id = fa2.film_id
INNER JOIN Ambiences AS a2 ON a2.id = fa2.ambience_id
INNER JOIN Films_Ambiences AS fa_all ON f.id = fa_all.film_id
WHERE (a1.ambienceName, a2.ambienceName) = ('scary','stupid')
GROUP BY f.id
HAVING COUNT(*) = 2
+----+-------+--------------+
| id | Title | ambiences |
+----+-------+--------------+
| 5 | Signs | scary,stupid |
+----+-------+--------------+
この場合、結合するa_all
必要はありません。アンビエンス名のリストは必要ないためです。必要なのはアンビエンスの数のみであり、結合するだけで取得できますfa_all
。