MySQL查询优化(需要太长)

MySQL查询优化(需要太长)

问题描述:

我用下面准备好的声明:MySQL查询优化(需要太长)

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(...)> =#计算错过会议的次数。 #是由用户设置的参数(我只是硬编码1进行测试)

+0

表结构是什么样的?你在使用索引吗? – Mike 2013-02-20 22:24:05

+4

你对查询做了一个EXPLAIN吗? – 2013-02-20 22:25:26

+0

应被编入索引:c_positions.POS_ID,c_members.CLUB_ID,USER_POINTS,c_users.POS_ID,MEM_ACADEMY ... – 2013-02-20 22:26:53

您需要在WHERE子句中使用所有字段的索引,这些字段的所有字段都已加入表中(您现在需要明确指出您的加入条件,您将获得笛卡尔联接)用于分组的字段和用于分类的所有字段。

最后一个问题是HAVING子句。因为它是一个计算值,所以你不能使用索引。如果这是一个查询,您将经常在系统中使用(即,不仅仅用于报告),您可以考虑添加一个可用作此过滤用标志的字段。无论何时在任何查询中设置c_done_meetings.MEDONE_ATTEND = 'u',都可以为成员或用户设置此标志,或者为此设置此标志,以便您可以在WHERE子句中筛选一个简单的字段。

除此之外,通过在子查询中获得具有u值的用户或成员的缩减列表,然后使用该子查询作为表,可能会获得更好的性能。

编辑:

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

+0

是的,我想我会继续做下去。我不会计算总和,而只是将它作为用户表中的一列添加。 – Andrew121007 2013-02-20 22:38:22

,在我蹦出来有些事情:

你使用像不少。尝试用你的php代码做一些事情,这样做没有必要。例如,mem_sex的选项数量有限。使前端成为单选按钮或下拉菜单,以便在可以使用=的地方发送值而不是类似的值。

二,如果你添加一个sum()到select子句和适当的group by子句,它应该运行得更快。这值得一试。

+0

我之所以喜欢它,是因为我希望可以选择将它留空,我认为SQL会比PHP更快,然后我会继续使用PHP构建查询。 – Andrew121007 2013-02-20 22:37:39

+0

如果你的sql说,哪里性爱?,它是空白你期望你的查询返回什么?另外,我并不是指使用php来编写sql,我的意思是使用php来分配更好的查询参数。 – 2013-02-20 22:43:27

您可以denormalise你的表,并添加字段c_members代表您 SUM(c_done_meetings.MEDONE_ATTEND = 'U')> = 1 但是,你需要随时更新该字段,更新c_done_meetings时(可用触发器完成)

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

+0

就像我在另一条评论中所说的,我使用LIKE,因为它有时是空白的。我可能会继续做一些PHP查询。 – Andrew121007 2013-02-20 22:40:37

+0

排除PHP中的条件时,它总是比SQL更快。因此,如果您排除所有LIKE条件并预先填充错过会议次数的值,则您的查询必须最多花费几秒钟。 – Yaroslav 2013-02-20 22:58:14