Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/76.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 规范化使我的查询速度变慢_Mysql_Sql - Fatal编程技术网

Mysql 规范化使我的查询速度变慢

Mysql 规范化使我的查询速度变慢,mysql,sql,Mysql,Sql,在正常化之前,我有一个名为Genrean的专栏,里面有动作、惊悚片、喜剧等价值观 现在,我通过创建流派表和movie2genre表规范了流派列 现在的问题是,我的查询更加复杂,实际上速度较慢 这两个查询主要搜索动作片和惊悚片 老问题 新查询 我做错什么了吗 更多信息: 电影 体裁 电影2 在没有相关查询的情况下尝试此操作。如果您关心性能,请检查两个查询的执行计划,并确保在新表上有适当的索引 SELECT * FROM movie2genre mg, Genre g, Movie m WHE

在正常化之前,我有一个名为Genrean的专栏,里面有动作、惊悚片、喜剧等价值观

现在,我通过创建流派表和movie2genre表规范了流派列

现在的问题是,我的查询更加复杂,实际上速度较慢

这两个查询主要搜索动作片和惊悚片

老问题

新查询

我做错什么了吗

更多信息:

电影

体裁

电影2


在没有相关查询的情况下尝试此操作。如果您关心性能,请检查两个查询的执行计划,并确保在新表上有适当的索引

 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