优化大型查询和表结构的问题

时间:2011-03-02 18:14:45

标签: mysql query-optimization subquery innodb normalization

我不是数据库专家,所以我已经存在了一段时间,尽可能多地阅读,并且由于社区的答案,我可以对我的查询和表格结构进行一些更改。即使在阅读了很多东西之后我也陷入了困境,所以我开始提出第一个问题。

我有一个网站,用户发布自己的故事。每个故事都可以有流派,警告,多个作者,分配多个角色等。

我们正在运行MySQL 5.x,表是InnoDB,用PHP编写的网站。使用GROUP_CONCAT在结果上每个故事返回一行。之前使用GROUP BY故事ID尝试过,但每个查询大约需要16秒才能完成,非常慢。使用这个新的,它们取0.175,但是,例如,如果WHERE中的类型不存在,则查询需要23秒!对于测试,每个表有100万条记录,作者表有150万条。我试图添加一些额外的索引来查看MySQL将使用哪一个。

我试图通过一对多的关系来规范化事物。在这里,我将只提供几个表,因为解决方案可能会涵盖整个问题。非常感谢任何帮助,感谢您的时间!

表格

CREATE TABLE `fanfiction_authors` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `penname` varchar(100) NOT NULL,
  `penname_url` varchar(100) NOT NULL,
  PRIMARY KEY (`uid`),
  KEY `penname_url` (`penname_url`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories` (
  `sid` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(200) NOT NULL,
  `sinopse` text NOT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_authors` (
  `sid` int(11) NOT NULL,
  `uid` int(11) NOT NULL,
  KEY `sid_uid` (`sid`,`uid`),
  KEY `sid` (`sid`),
  KEY `uid` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_genres` (
  `key_id` int(11) NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL,
  `genre_id` int(11) NOT NULL,
  PRIMARY KEY (`key_id`),
  KEY `sid` (`sid`),
  KEY `genre_id` (`genre_id`),
  KEY `sid_genreid` (`sid`,`genre_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_stats` (
  `sid` int(11) NOT NULL,
  `reviews` int(11) NOT NULL,
  `recomendacoes` int(11) NOT NULL,
  PRIMARY KEY (`sid`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- --------------------------------------------------------

CREATE TABLE `fanfiction_stories_warnings` (
  `key_id` int(11) NOT NULL AUTO_INCREMENT,
  `sid` int(11) NOT NULL,
  `warning_id` int(11) NOT NULL,
  PRIMARY KEY (`key_id`),
  KEY `sid` (`sid`),
  KEY `warning_id` (`warning_id`),
  KEY `warningid_sid` (`sid`,`warning_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1000000 ;

----

查询

SELECT  
    st.sid, st.title, st.sinopse,  
    (SELECT GROUP_CONCAT(CAST(genre_id AS CHAR)) FROM fanfiction_stories_genres WHERE sid = st.sid) as genres,  
    stats.reviews, stats.recomendacoes,  
    (SELECT GROUP_CONCAT(CAST(warning_id AS CHAR)) FROM fanfiction_stories_warnings WHERE sid = st.sid) as warnings_ids  
FROM  
    fanfiction_stories AS st  
    LEFT JOIN fanfiction_stories_stats AS stats ON st.sid = stats.sid  
WHERE  
    st.sid IN (SELECT sid FROM fanfiction_stories_warnings WHERE warning_id = 5) AND  
    st.sid IN (SELECT sid FROM fanfiction_stories_genres WHERE genre_id = 300)  
ORDER BY  
    st.sid ASC  
LIMIT 20  

我无法让我的解释在这里可读,所以我将printscreen上传到Dropbox。无法嵌入图片,因为我在这里是个菜鸟,对不起。

这是我们拥有有效流派时的解释(您将找到流派编号为300的故事)。

explain http://dl.dropbox.com/u/14508898/Printscreen/stackoverflow_explain_print_001.PNG

这是我们有一个无效类型时的解释(你不会找到类型为900的故事)。

invalid genre explain http://dl.dropbox.com/u/14508898/Printscreen/stackoverflow_explain_print_002.PNG

你能帮帮我吗?我的标准化是否正确?我做错了什么?

提前致谢!

1 个答案:

答案 0 :(得分:1)

您可以使用JOIN来保存2个内部选择,这肯定会在两种情况下加快速度(genre_id = 300genre_id = 900)。

SELECT  
    st.sid, st.title, st.sinopse,  
    (SELECT GROUP_CONCAT(CAST(genre_id AS CHAR)) FROM fanfiction_stories_genres WHERE sid = st.sid) as genres,  
    stats.reviews, stats.recomendacoes,  
    (SELECT GROUP_CONCAT(CAST(warning_id AS CHAR)) FROM fanfiction_stories_warnings WHERE sid = st.sid) as warnings_ids  
FROM  
    fanfiction_stories AS st  
    LEFT JOIN fanfiction_stories_stats AS stats ON st.sid = stats.sid  
    JOIN fanfiction_stories_warnings w ON st.sid = w.sid AND w.warning_id = 5
    JOIN fanfiction_stories_genres g ON st.sid = g.sid AND g.genre_id = 300
GROUP BY st.sid
ORDER BY st.sid ASC  
LIMIT 20