代码之家  ›  专栏  ›  技术社区  ›  Sloan Thrasher

mysql查询耗时太长,未使用索引

  •  1
  • Sloan Thrasher  · 技术社区  · 6 年前

    我有两张桌子,一张大约700行,另一张超过100000行。

    我的查询是在第一个表中查找第二个表中不存在电子邮件地址的所有行。

    SELECT a.* 
    FROM `csv_import_temp` a FORCE INDEX FOR JOIN (`imp_email`)
    LEFT JOIN `xlefz_mailer_Contacts` b FORCE INDEX FOR JOIN (`idx_contact_email`)
    ON a.`contact_email` like b.`contact_email`
    WHERE b.`contact_email` is null
    

    两个表中都有联系人电子邮件的索引。查询的解释显示,即使在添加了force index子句之后,也只使用了其中一个索引。

    id  | select_type  | table  | type   | possible_keys  | key                | key_len  | ref  | rows    | Extra                    | 
     1  | SIMPLE       | a      | ALL    | NULL           | NULL               | NULL     | NULL | 292     | 
     1  | SIMPLE       | b      | index  | NULL           | idx_contact_email  | 228      | NULL | 106149  | Using where; Using index
    

    目前,查询只需6秒多的时间即可运行。

    如何使查询同时使用这两个索引?

    第一张桌子:

    CREATE TABLE `csv_import_temp` (
     `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
     `contact_lname` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Last or Family name',
     `contact_email` varchar(75) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'Email Address',
     `contact_state` varchar(20) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'State or provence',
     `contact_country` varchar(25) CHARACTER SET utf8 DEFAULT 'USA' COMMENT 'Country Name or Code',
     `contact_oktoemail` enum('Yes','No') COLLATE utf8_unicode_ci DEFAULT 'Yes' COMMENT 'Yes if OK to send Email',
     `contact_oktofax` enum('Yes','No') COLLATE utf8_unicode_ci DEFAULT 'Yes' COMMENT 'Yes of Ok to send Fax',
     `contact_oktomail` enum('Yes','No') COLLATE utf8_unicode_ci DEFAULT 'Yes' COMMENT 'Yes of OK to send mail',
     `contact_status` enum('Ok','Unsub','Bounced','BouncedUpdated','isAOL') COLLATE utf8_unicode_ci DEFAULT 'Ok' COMMENT 'Ok - can contact via email, Unsub if unsubscribe request has been received. Bounced = Mail sent to email address bounced.',
     PRIMARY KEY (`id`),
     KEY `imp_email` (`contact_email`)
    ) ENGINE=MyISAM AUTO_INCREMENT=293 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
    

    第二张桌子:

    CREATE TABLE `xlefz_mailer_Contacts` (
     `contact_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
     `contact_created` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Timestamp when record was created',
     `contact_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Timestamp when record was last modified',
     `manual_update` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'If 1, has been manually updated',
     `manual_updateDT` timestamp NULL DEFAULT NULL COMMENT 'Timestamp of last manual update',
     `contact_import_id` int(11) DEFAULT NULL,
     `contact_company` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Company Name',
     `contact_title` varchar(20) CHARACTER SET utf8 DEFAULT NULL COMMENT 'Mr., Ms., Dr., etc.',
     `contact_fname` varchar(50) CHARACTER SET utf8 NOT NULL DEFAULT '' COMMENT 'First or given name',
     `contact_lname` varchar(50) COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT 'Last or Family name',
     `contact_email` varchar(75) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Email Address',
     `contact_email_name` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The mart of an email address before the @',
     `contact_email_domain` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'The mart of an email address after the @',
     `contact_addr1` varchar(50) COLLATE utf8_unicode_ci DEFAULT '' COMMENT '1st line of mailing address',
     `contact_addr2` varchar(50) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'Apartment, suite, etc.',
     `contact_city` varchar(50) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'City',
     `contact_state` varchar(20) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'State or provence',
     `contact_zip` varchar(15) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'Postal code',
     `contact_country` varchar(25) CHARACTER SET utf8 DEFAULT NULL COMMENT 'Country Name or Code',
     `contact_phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'Voice Phone',
     `contact_fax` varchar(20) COLLATE utf8_unicode_ci DEFAULT '' COMMENT 'Fax Phone',
     `contact_oktoemail` enum('Yes','No') COLLATE utf8_unicode_ci DEFAULT 'Yes' COMMENT 'Yes if OK to send Email',
     `contact_oktofax` enum('Yes','No') COLLATE utf8_unicode_ci DEFAULT 'Yes' COMMENT 'Yes of Ok to send Fax',
     `contact_oktomail` enum('Yes','No') COLLATE utf8_unicode_ci DEFAULT 'Yes' COMMENT 'Yes of OK to send mail',
     `contact_notes` varchar(1000) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'Notes regarding contact.',
     `contact_status` enum('Ok','Unsub','Bounced','BouncedUpdated','isAOL') COLLATE utf8_unicode_ci DEFAULT 'Ok' COMMENT 'Ok - can contact via email, Unsub if unsubscribe request has been received. Bounced = Mail sent to email address bounced. BouncedUpdated - indicates that the contact has been copied and updated with a new email address',
     PRIMARY KEY (`contact_id`),
     KEY `idx_contact_email` (`contact_email`),
     KEY `idx_contact_city` (`contact_city`),
     KEY `idx_contact_country` (`contact_country`),
     KEY `idx_contact_oktoemail` (`contact_oktoemail`),
     KEY `idx_contact_oktofax` (`contact_oktofax`),
     KEY `idx_contact_oktomail` (`contact_oktomail`),
     KEY `contact_import_id` (`contact_import_id`),
     KEY `idx_contact_oktoemailstatus` (`contact_oktoemail`,`contact_status`),
     KEY `contact_email_name` (`contact_email_name`,`contact_email_domain`),
     KEY `idx_mailer_contacts_manualupdate` (`manual_update`),
     KEY `idx_mailer_contact_manualupdateDT` (`manual_updateDT`)
    ) ENGINE=MyISAM AUTO_INCREMENT=128829 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
    
    1 回复  |  直到 6 年前
        1
  •  4
  •   Øystein Grøvlen    6 年前

    要澄清问题中的一些误解:

    1. 在索引嵌套循环联接中,索引将仅用于联接的第二个表。因此,在第一个表上指定join的force索引将没有任何效果。
    2. 当explain说类型是“index”时,并不意味着索引用于连接查找。“索引”表示完全索引扫描。在本例中使用索引是因为它包含此表所需的所有列。换句话说,它是 覆盖指数 . (“explain”额外一列中的“using index”实际上是指“using index only”)
    3. 当联接条件基于 喜欢 ,无法使用索引。列可能包含通配符,这些通配符无法通过索引查找找到匹配项。
    4. 比较使用 = 不一定区分大小写。这将取决于使用的排序规则。在您的示例中,两个涉及的列都是用不区分大小写的排序规则(utf8_unicode_ci)定义的。因此,比较将不区分大小写。
    5. 弥撒的使用;-)

    总之,应该对联接条件使用相等条件,或者可以将查询重写为以下imho更简单的查询:

    SELECT * 
    FROM csv_import_temp
    WHERE contact_email NOT IN (
        SELECT contact_email FROM xlefz_mailer_Contacts);