MySQL查询优化 - 提高速度和效率

时间:2012-06-28 16:06:53

标签: mysql database optimization

我想在数据库中收到一些电子邮件,每封电子邮件都有一个状态。所有可能的状态都存储在表中,它们都具有权限(例如显示,编辑,删除等)。这些电子邮件不是通过网站拥有权限的用户,而是用户添加的电子邮件列表。

这是表结构:

电子邮件表

    CREATE TABLE IF NOT EXISTS `email__email` (
    `email_id` int(11) NOT NULL AUTO_INCREMENT,
    `created` timestamp NULL DEFAULT NULL,
    `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `user_fk` int(11) NOT NULL,
    `status_fk` tinyint(2) NOT NULL,
    `language` enum('fr','en') COLLATE utf8_unicode_ci DEFAULT NULL,
    `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `firstName` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `lastName` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
    `companyName` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `gender` enum('f','m') COLLATE utf8_unicode_ci DEFAULT NULL,
    PRIMARY KEY (`email_id`),
    UNIQUE KEY `user_email` (`user_fk`,`email`),
    KEY `user_fk` (`user_fk`),
    KEY `created` (`created`),
    KEY `status_fk` (`status_fk`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3031492 ;

状态表

    CREATE TABLE IF NOT EXISTS `email__status` (
    `status_id` int(11) NOT NULL AUTO_INCREMENT,
    `name_fr` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
    `name_en` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
    `description_fr` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
    `description_en` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
    `permShow` tinyint(1) NOT NULL DEFAULT '0',
    `permSend` tinyint(1) NOT NULL DEFAULT '0',
    `permEdit` tinyint(1) NOT NULL DEFAULT '0',
    `permDelete` tinyint(1) NOT NULL DEFAULT '0',
    `permImport` tinyint(1) NOT NULL DEFAULT '0',
    PRIMARY KEY (`status_id`)
    ) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=7 ;

这是使用EXPLAIN的慢查询:

    SELECT EE.*, ES.name_fr AS statusName, ES.description_fr AS statusDescription, ES.permShow, ES.permSend, ES.permEdit, ES.permDelete, ES.permImport
    , (SELECT GROUP_CONCAT(CONVERT(CONCAT(GC.name, '~', GC.group_id), CHAR(255)) SEPARATOR ',') FROM `group` GC INNER JOIN group_email GEC ON GEC.group_fk = GC.group_id WHERE GEC.email_fk = EE.email_id AND GC.deleted = 0) AS groups
    FROM `email__email` EE
    INNER JOIN email__status ES ON EE.status_fk = ES.status_id
    WHERE 1 = 1
    AND EE.user_fk = 54
    AND ES.permShow = 1
    ORDER BY EE.email_id DESC LIMIT 15


    EXTRA   ID  KEY KEY_LEN POSSIBLE_KEYS   REF ROWS    SELECT_TYPE TABLE   TYPE
    Using temporary; Using filesort 1   user_email  4   user_email,user_fk,status_fk    const   180681  PRIMARY EE  ref
    Using where; Using join buffer  1   [empty string]  [empty string]  PRIMARY [empty string]  6   PRIMARY ES  ALL
    Using index 2   email_fk    4   group_email,group_fk,email_fk   mailing_dev.EE.email_id 1   DEPENDENT SUBQUERY  GEC ref
    Using where 2   PRIMARY 4   PRIMARY mailing_dev.GEC.group_fk    1   DEPENDENT SUBQUERY  GC  eq_ref

以下是使用EXPLAIN的快速查询:

    SELECT EE.*
    , (SELECT GROUP_CONCAT(CONVERT(CONCAT(GC.name, '~', GC.group_id), CHAR(255)) SEPARATOR ',') FROM `group` GC INNER JOIN group_email GEC ON GEC.group_fk = GC.group_id WHERE GEC.email_fk = EE.email_id AND GC.deleted = 0) AS groups
    FROM `email__email` EE
    WHERE 1 = 1 
    AND EE.user_fk = 54
    AND EXISTS(SELECT permShow FROM email__status WHERE status_id = EE.status_fk AND permShow = 1)
    ORDER BY EE.email_id DESC LIMIT 15


    EXTRA   ID  KEY KEY_LEN POSSIBLE_KEYS   REF ROWS    SELECT_TYPE TABLE   TYPE
    Using where 1   PRIMARY 4   user_email,user_fk  [empty string]  270 PRIMARY EE  index
    Using where 3   PRIMARY 4   PRIMARY mailing_dev.EE.status_fk    1   DEPENDENT SUBQUERY  email__status   eq_ref
    Using index 2   email_fk    4   group_email,group_fk,email_fk   mailing_dev.EE.email_id 1   DEPENDENT SUBQUERY  GEC ref
    Using where 2   PRIMARY 4   PRIMARY mailing_dev.GEC.group_fk    1   DEPENDENT SUBQUERY  GC  eq_ref

两个查询之间存在很大差异,但第二个查询没有给出我需要获取的两个重要列。我可以做子查询来获取它们,就像连接一样,但是,我仍然不希望每个子查询都有很多子查询...有什么想法可以改进这个吗?

由于

1 个答案:

答案 0 :(得分:0)

email__email.status_fk是一个tinyint,但email__status.status_id是一个int(11)。

这可能会影响你的INNER JOIN。更改一种或另一种数据类型,然后重试。