MySQL查询优化(需要太长时间)

时间:2013-02-20 22:22:13

标签: php sql performance optimization

我使用以下准备好的声明:

    SELECT * 
    FROM 
        c_members,c_users,c_positions,c_done_meetings
    WHERE 
        c_positions.POS_ID=c_users.POS_ID 
        AND c_members.CLUB_ID = ? 
        AND USER_POINTS >= ?
        AND USER_POINTS <= ? 
        AND c_users.POS_ID LIKE ? 
        AND MEM_ACADEMY LIKE ? 
        AND MEM_SEX LIKE ? 
        AND MEM_GRADELVL LIKE ? 
        AND MEM_GPA >= ? 
        AND MEM_GPA <= ? 
        AND MEM_ARCHIVE = 0 
GROUP BY 
    c_members.MEM_ID, c_members.CLUB_ID 
HAVING 
    SUM(c_done_meetings.MEDONE_ATTEND = 'u') >= 1 
ORDER BY 
    USER_POINTS DESC

但是,此查询需要21.971405982971秒才能加载111条记录。当我删除“有SUM(...)”子句时,性能会提高100%。有没有办法可以更好地优化它?

编辑:(表格结构)

    CREATE TABLE IF NOT EXISTS `c_done_meetings` (
  `MEM_ID` int(11) NOT NULL,
  `CLUB_ID` int(11) NOT NULL,
  `MEETING_ID` int(11) NOT NULL,
  `MEDONE_ATTEND` varchar(1) NOT NULL COMMENT 'E=excused, U=unexcused, P=present',
  UNIQUE KEY `unique` (`MEM_ID`,`CLUB_ID`,`MEETING_ID`),
  KEY `MEETING_ID` (`MEETING_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    CREATE TABLE IF NOT EXISTS `c_members` (
  `MEM_ID` int(11) NOT NULL,
  `CLUB_ID` int(11) NOT NULL,
  `MEM_FIRST` varchar(50) NOT NULL,
  `MEM_MIDDLE` varchar(50) DEFAULT NULL,
  `MEM_LAST` varchar(50) NOT NULL,
  `MEM_SEX` tinyint(1) NOT NULL COMMENT '0-Male 1-Female',
  `MEM_EMAIL` varchar(100) DEFAULT NULL,
  `MEM_GRADELVL` int(11) NOT NULL,
  `MEM_ACADEMY` varchar(50) DEFAULT '',
  `MEM_GPA` double DEFAULT '0',
  `MEM_ADDRESS` varchar(500) DEFAULT NULL,
  `MEM_CITY` varchar(100) DEFAULT NULL,
  `MEM_STATE` varchar(100) DEFAULT NULL,
  `MEM_ZIP` int(11) DEFAULT NULL,
  `MEM_TELEPHONE` varchar(25) DEFAULT NULL,
  `MEM_AP` tinyint(1) NOT NULL,
  `MEM_HONORS` tinyint(1) NOT NULL,
  `MEM_ESOL` tinyint(1) NOT NULL,
  `MEM_HISP` tinyint(1) NOT NULL,
  `MEM_WHITE` tinyint(1) NOT NULL,
  `MEM_MULTI` tinyint(1) NOT NULL,
  `MEM_NATIVE` tinyint(1) NOT NULL,
  `MEM_BLACK` tinyint(1) NOT NULL,
  `MEM_ASIAN` tinyint(1) NOT NULL,
  `MEM_EXTRA` varchar(10000) DEFAULT NULL,
  `MEM_ARCHIVE` tinyint(1) NOT NULL DEFAULT '0',
  UNIQUE KEY `MEM_ID` (`MEM_ID`,`CLUB_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    CREATE TABLE IF NOT EXISTS `c_positions` (
  `POS_ID` int(11) NOT NULL AUTO_INCREMENT,
  `CLUB_ID` int(11) NOT NULL,
  `POS_NAME` varchar(20) NOT NULL,
  `POS_DESC` varchar(500) NOT NULL,
  `POS_ADMIN` tinyint(1) NOT NULL,
  `POS_ATN_VIEW` tinyint(1) NOT NULL,
  `POS_ATN_CHKIN` tinyint(1) NOT NULL,
  `POS_ATN_FINALIZE` tinyint(1) NOT NULL,
  `POS_MEM_VIEW` tinyint(1) NOT NULL,
  `POS_MEM_ADD` tinyint(1) NOT NULL,
  `POS_MEM_EDIT` tinyint(1) NOT NULL,
  `POS_POS_VIEW` tinyint(1) NOT NULL,
  `POS_POS_ADD` tinyint(1) NOT NULL,
  `POS_POS_EDIT` tinyint(1) NOT NULL,
  `POS_MEET_VIEW` tinyint(1) NOT NULL,
  `POS_MEET_ADD` tinyint(1) NOT NULL,
  `POS_MEET_EDIT` tinyint(1) NOT NULL,
  `POS_EVENT_VIEW` tinyint(1) NOT NULL,
  `POS_EVENT_ADD` tinyint(1) NOT NULL,
  `POS_EVENT_EDIT` tinyint(1) NOT NULL,
  `POS_EVENT_UPDATE` tinyint(1) NOT NULL,
  `POS_REPORT_VIEW` tinyint(1) NOT NULL,
  `POS_ARCHIVE_VIEW` tinyint(1) NOT NULL,
  `POS_ANNOUNCEMENTS` tinyint(1) NOT NULL,
  `POS_WEB_CUSTOM` tinyint(1) NOT NULL,
  PRIMARY KEY (`POS_ID`),
  UNIQUE KEY `UNIQUE_NAME` (`CLUB_ID`,`POS_NAME`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=14 ;

    CREATE TABLE IF NOT EXISTS `c_users` (
  `MEM_ID` int(11) NOT NULL,
  `CLUB_ID` int(11) NOT NULL,
  `USER_PIN` int(11) NOT NULL,
  `USER_POINTS` double NOT NULL,
  `POS_ID` int(11) NOT NULL,
  `USER_ARCHIVE` tinyint(1) NOT NULL DEFAULT '0',
  UNIQUE KEY `MEM_ID` (`MEM_ID`,`CLUB_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

编辑2: 是的,所有ID都被编入索引,SUM(...)&gt; =#计算错过的会议数。 #是用户设置的参数(我只是硬编码1进行测试)

3 个答案:

答案 0 :(得分:0)

突然出现的一些事情:

你使用得非常多。尝试用你的PHP代码做一些事情,这样就没有必要了。例如,mem_sex具有有限数量的选择。将前端设为单选按钮或下拉菜单,以便您可以使用=而不是类似的方式发送值。

二,如果你在select子句和相应的group by子句中添加sum(),它应该运行得更快。值得一试。

答案 1 :(得分:0)

您需要在所有字段上的索引中使用WHERE子句中使用的所有字段,在表格所连接的所有字段上(您需要立即明确说明您的连接条件,您将获得笛卡尔连接)用于分组和用于排序的所有字段。

最后一个问题是那里的HAVING子句。你根本无法使用索引,因为它是一个计算值。如果这是您经常在系统中使用的查询(即不仅仅用于报告),您可以考虑添加一个字段,您可以将其用作此过滤目的的标志。无论何时在任何查询中设置c_done_meetings.MEDONE_ATTEND = 'u',您还可以为成员或用户或与之关联的任何内容设置此标志,以便在WHERE子句中有一个简单的字段可以过滤。

除此之外,您实际上可以通过在子选择中获得具有u值的用户或成员的简化列表来获得更好的性能,然后使用该子选择作为表加入。

编辑:

在看到您的实际表结构后,我可以清楚地看到您需要添加索引的位置。我也想知道为什么你的表c_usersc_members具有相同的主键。为什么这些不只是一张桌子?

答案 2 :(得分:0)

您可以对表格进行非规范化处理,并将字段添加到代表您的表格的 c_members     SUM(c_done_meetings.MEDONE_ATTEND ='u')&gt; = 1 但是,在更新 c_done_meetings (可以使用触发器完成)时,您需要始终更新该字段

还要尽量避免LIKE条件。使用= insead(至少可以用于SEX)