简体   繁体   中英

MySQL query optimization - increase speed and efficiency

I want to get some emails in a database and each email have a status. All the possible status are stock in a table where they all have permissions (such as show, edit, delete, etc.). Those emails are not users with permissions trough a site but a list of emails a user have added.

Here is the tables structure:

Email table

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 ;

Status table

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 ;

Here is the slow query with the 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

Here is a fast query with the 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

There is a big difference between both queries but the second one doesn't give me two important columns that I need to fetch. I can do subqueries to fetch them like a join would do but still, I don't want to have a lot of subqueries for each... any ideas to improve this ?

Thanks

email__email.status_fk is a tinyint, but email__status.status_id is an int(11).

This probably is fouling up your INNER JOIN. Change one or the other data type and try again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM