在规范化之前,我有一个名为genre
的列,它包含“动作,惊悚,喜剧”等值
现在我通过创建genre
和movie2genre
表来规范化类型列。
现在的问题是我的查询更复杂,实际上更慢
这两个查询基本上都是搜索动作和惊悚片的电影
旧查询
select title, genre from movie where genre like '%action%' and genre like '%thriller%'
0.062 sec duration / 0.032 sec fetch
新查询
SELECT movie.title, movie.genre
FROM Movie
Where
EXISTS (
select *
from movie2genre
JOIN Genre on Genre.id = movie2genre.GenreId
where Movie.id = movie2genre.MovieId
and genre in ('action', 'thriller')
)
0.328 sec duration / 0.078 sec fetch
我做错了吗?
更多信息:
电影
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | NULL | auto_increment |
| Title | varchar(345) | YES | | NULL | |
ETC....
类型
+---------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+----------------+
| genreid | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+----------------+.
movie2genre
+---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| movieid | int(11) | YES | | NULL | |
| genreid | int(11) | YES | | NULL | |
+---------+---------+------+-----+---------+-------+
答案 0 :(得分:0)
在没有相关查询的情况下尝试此操作(如果您担心性能,请检查两个查询的执行计划)还要确保新表上有适当的索引。
SELECT *
FROM movie2genre mg, Genre g, Movie m
WHERE m.id = mg.MovieId
AND g.id = mg.GenreId
AND g.genre in ('action', 'thriller')
答案 1 :(得分:0)
首先,您的两个查询不相同。较新的版本执行or
而不是and
,因此时间上的差异可能只是返回更大的结果集。此外,您的新查询引用movie.genre
,这是一个在规范化数据库中不存在的列。
你好像要求:
select m.title
from Movie m
where exists (select 1
from movie2genre m2g JOIN
Genre g
on g.id = m2g.GenreId
where m.id = m2g.MovieId and g.genre = 'action'
) and
exists (select 1
from movie2genre m2g JOIN
Genre g
on g.id = m2g.GenreId
where m.id = m2g.MovieId and g.genre = 'thriller'
);
不可否认,您可能不会认为这解决了“并发症”问题。抛开这一点,你需要有索引才能正常工作。你有“明显”的索引:movie2genre(MovieId, GenreId)
和genre(GenreId)
吗?
其次,您的数据不是特别大(根据查询的持续时间判断)。因此,全表扫描可能比使用这些表加入和过滤更有效。随着数据库的增长,规范化方法通常会更快。
更等效的查询是:
select m.title, group_concat(g.genre)
from movies m join
movie2genre m2g
on m.movieid = m2g.movieid join
genre g
on g.genreid = m2g.genreid
group by m.title
having sum(g.genre = 'action') > 0 and sum(g.genre = 'thriller') > 0;
由于您的特定查询的性质 - 您需要获取电影上的所有类型,以便您无法对其进行过滤 - 此特定查询的执行效果可能不如非标准化版本。
顺便说一句,规范化更多的是保持数据一致而不是加速查询。规范化数据库需要更多连接操作。索引可以帮助提高性能,但仍然可以进行连接。在某些情况下,表格本身比非标准化表格更大。并且,规范化数据库可能需要聚合,而非规范化数据库不需要聚合。所有这些都会影响性能,这就是为什么在许多决策支持体系结构中,中央数据库是规范化的,但特定于应用程序的数据库却没有。
答案 2 :(得分:0)
索引在进行连接时非常重要(子查询往往会丢失索引)。 我建议尝试两种方法。
首先,您将电影加入movie2genre,然后为您正在检查的每个电影加入一个流派。那么索引应该很快。
SELECT movie.title,
movie.genre
FROM Movie
INNER JOIN movie2genre
ON Movie.id = movie2genre.MovieId
INNER JOIN Genre G1
ON G1.id = movie2genre.GenreId
AND G1.genre = 'action'
INNER JOIN Genre G2
ON G2.id = movie2genre.GenreId
AND G2.genre = 'thriller'
另一种方法是使用IN,并使用聚合COUNT函数来检查找到的类型数是否与预期数相同。
SELECT movie.title,
movie.genre
FROM Movie
INNER JOIN movie2genre
ON Movie.id = movie2genre.MovieId
INNER JOIN Genre
ON Genre.id = movie2genre.GenreId
AND Genre.genre IN ('action', 'thriller')
GROUP BY movie.title, movie.genre
HAVING COUNT(DISTINCT genreid) = 2
我更喜欢第一种解决方案,但是在代码中设置SQL会有点复杂(即,SQL根据类型的数量而变化很大),并且可能受到表的最大数量的限制如果你要检查很多类型,请加入。