使用关键字搜索谓词LIKE '%pattern%'
肯定会导致性能下降,因为它会强制进行表扫描。
进行关系除法查询(即仅匹配所有三个条件都匹配的电影)的最佳方法是为每个条件查找单独的行,然后将它们连接在一起。
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
添加一个Films_Ambiences
和。Ambiences
你应该有一个索引ambienceName
,然后所有三个查找都会更有效率。
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;
有没有办法改变这个查询,以便结果只有具有所需氛围但没有更多的电影?
上面的查询应该这样做。
我认为,该查询的作用是寻找包含给定氛围的电影(因此它也会找到具有更多氛围的电影)。
是的,除非它匹配搜索条件中的所有三种环境,否则查询不匹配电影。但电影可能有搜索条件之外的其他氛围,并且电影的所有氛围(搜索条件中的氛围加上其他)都收集为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 |
+----+--------------+-------------------------+
顺便说一句,这是显示索引用法的说明:
+----+-------------+--------+--------+----------------------+--------------+---------+-----------------------------+------+-----------------------------------------------------------+
| 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 |
+----+-------------+--------+--------+----------------------+--------------+---------+-----------------------------+------+-----------------------------------------------------------+
我有一部恐怖、有趣、愚蠢的电影。当我搜索一部只可怕的电影时,愚蠢的我无论如何都会得到film1。如果我不想要那个怎么办?
哦,好吧,我完全不明白你的意思,在这类问题中这是一个不寻常的要求。
这是一个解决方案:
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 |
+----+-------+--------------+
在这种情况下不需要加入 to a_all
,因为我们不需要环境名称列表,我们只需要环境的数量,我们可以通过加入来获得fa_all
。