规范化使我的查询变慢

时间:2014-11-04 11:25:49

标签: mysql sql

在规范化之前,我有一个名为genre的列,它包含“动作,惊悚,喜剧”等值

现在我通过创建genremovie2genre表来规范化类型列。

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

这两个查询基本上都是搜索动作和惊悚片的电影

旧查询

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    |       |
+---------+---------+------+-----+---------+-------+

3 个答案:

答案 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根据类型的数量而变化很大),并且可能受到表的最大数量的限制如果你要检查很多类型,请加入。