Mysql 规范化使我的查询速度变慢
在正常化之前,我有一个名为Genrean的专栏,里面有动作、惊悚片、喜剧等价值观 现在,我通过创建流派表和movie2genre表规范了流派列 现在的问题是,我的查询更加复杂,实际上速度较慢 这两个查询主要搜索动作片和惊悚片 老问题 新查询 我做错什么了吗 更多信息: 电影 体裁 电影2Mysql 规范化使我的查询速度变慢,mysql,sql,Mysql,Sql,在正常化之前,我有一个名为Genrean的专栏,里面有动作、惊悚片、喜剧等价值观 现在,我通过创建流派表和movie2genre表规范了流派列 现在的问题是,我的查询更加复杂,实际上速度较慢 这两个查询主要搜索动作片和惊悚片 老问题 新查询 我做错什么了吗 更多信息: 电影 体裁 电影2 在没有相关查询的情况下尝试此操作。如果您关心性能,请检查两个查询的执行计划,并确保在新表上有适当的索引 SELECT * FROM movie2genre mg, Genre g, Movie m WHE
在没有相关查询的情况下尝试此操作。如果您关心性能,请检查两个查询的执行计划,并确保在新表上有适当的索引
SELECT *
FROM movie2genre mg, Genre g, Movie m
WHERE m.id = mg.MovieId
AND g.id = mg.GenreId
AND g.genre in ('action', 'thriller')
首先,您的两个查询不同。较新的版本执行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'
);
诚然,你可能不会认为这解决了复杂的问题。撇开这一点不谈,您需要有索引才能很好地工作。你有明显的索引:movie2genreMovieId、GenreId和genreGenreId吗
其次,从查询的持续时间来看,您的数据不是特别大。因此,全表扫描可能比连接和过滤这些表更有效。随着数据库的增长,规范化方法通常会更快
更等效的查询是:
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;
由于特定查询的性质——您需要获取电影中的所有类型,因此无法对其进行过滤——此特定查询的性能可能不如非规范化版本
顺便说一句,规范化更多的是保持数据的一致性,而不是加速查询。规范化数据库需要更多的联接操作。索引有助于提高性能,但仍然需要进行连接。在某些情况下,表本身比未规范化的表单大。而且,规范化数据库可能需要聚合,而非规范化数据库则不需要聚合。所有这些都会影响性能,这就是为什么在许多决策支持体系结构中,中央数据库是标准化的,而特定于应用程序的数据库则不是标准化的。在执行连接和子查询时,索引非常重要,因为它们往往会丢失索引。 我建议尝试两种方法 首先,您将电影加入到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'
另一种方法是在中使用,并使用聚合计数函数检查找到的流派数量是否与预期数量相同
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
我更喜欢第一种解决方案,但在代码ie中设置SQL要复杂一些,SQL根据类型的数量变化很大,如果检查大量类型,可能会受到最大表连接数的限制。您在movie2genre中设置了genreid索引吗?两种查询都不同,在第一个查询中,你有一个and cond,而在第二个查询中,你有一个in子句,它等于or子句,因此是不同的。这是更快的,但我试图找到至少有动作和惊悚片的电影类型。这意味着它们必须是动作片和惊悚片,它们可以是动作片和喜剧片,但它们至少需要动作片和惊悚片。您可以共享表中的示例数据。请把它添加到问题中。
SELECT *
FROM movie2genre mg, Genre g, Movie m
WHERE m.id = mg.MovieId
AND g.id = mg.GenreId
AND g.genre in ('action', 'thriller')
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'
);
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;
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'
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