MySQL 查询时间太长,未使用索引

标签 mysql query-optimization

我有两个表,一个有大约 700 行,另一个有 100,000 多行。

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

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

两个表中都有一个关于 contact_email 的索引。查询的解释显示仅使用了一个索引,即使在我添加了 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. 在索引嵌套循环连接中,索引将仅用于连接的第二个表。因此,在第一个表上指定 FORCE INDEX FOR JOIN 将无效。
  2. 当 EXPLAIN 说类型是“索引”时,并不意味着索引用于连接查找。 “index”表示全索引扫描。在这种情况下使用索引是因为它包含该表所需的所有列。换句话说,它是一个覆盖索引。 (EXPLAIN Extra 列中的“Using index”真正的意思是“using index only”)
  3. 当连接条件基于LIKE 时,不能使用索引。这些列可能包含无法通过索引查找找到匹配项的通配符。
  4. 使用= 的比较不一定区分大小写。这将取决于使用的排序规则。在您的情况下,两个涉及的列都是使用不区分大小写的排序规则 (utf8_unicode_ci) 定义的。因此,比较将不区分大小写。
  5. MyISAM 的使用 ;-)

总而言之,您应该为连接条件使用相等性,或者您可以将查询重写为以下恕我直言,更简单的查询:

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

关于MySQL 查询时间太长,未使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50667749/

相关文章:

mysql - 子查询优化

mysql - MySQL 是否消除了 SELECT 和 HAVING/GROUP BY 子句之间的公共(public)子表达式

mysql - SQL从/到特定用户获取最后消息

PHP 日期() 函数

mysql - 没有外键的连接表

php - 在单个 SELECT 查询中设置 enable_seqscan = off

c# - MSSQL 快速更新

mysql - 存储过程不更新 MySQL 的表

mysql查询获取每个部门前两名的工资

MySQL - 优化查询以使用临时删除和使用文件排序