MySQL查询减速并添加了另一个AND子句

时间:2014-06-27 16:15:22

标签: mysql sql query-optimization

我在Symfony2项目中使用Doctrine2,ORM产生以下查询:

SELECT COUNT(p0_.id) AS sclr0 
FROM Purchase p0_ 
LEFT JOIN USER u1_ ON p0_.user_id = u1_.id 
LEFT JOIN Config c2_ ON u1_.id = c2_.id AND (c2_.deletedAt IS NULL) 
WHERE (u1_.organization_id = 7) AND (p0_.deletedAt IS NULL);

它返回176336并启动服务器30秒。

如果删除AND (p0_.deletedAt IS NULL)条款则需要0.1秒。为什么AND子句(不是OR!),它应该使resutset始终< =没有它的情况下,将它减慢到如此之大? 为了证明这一点,这是使用AND:

查询后SELECT PROFILES;输出的屏幕截图

Profiler output WITH AND

以下是完全相同查询的输出,但没有AND (p0_.deletedAt IS NULL),请注意下面附加较新的查询(请参阅Query_ID = 5):

Profiler output WITHOUT AND

为了确保这不是缓存问题,我启动了另一个没有AND (p0_.deletedAt IS NULL)且添加了SQL_NO_CACHE的相同查询(请参阅Query_ID = 8)

这在Windows mysql Ver 14.14 Distrib 5.6.14, for Win64 (x86_64)和Ubuntu 5.5.37-0ubuntu0.12.04.1-log都可以重现。以下是Linux更强大的服务器上的相同实验:

+----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration   | Query                                                                                                                                                                                                                                         |
+----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|        1 | 0.00025925 | SELECT SQL_NO_CACHE COUNT(p0_.id) AS sclr0
FROM Purchase p0_
LEFT JOIN USER u1_ ON p0_.user_id = u1_.id
LEFT JOIN Config c2_ ON u1_.id = c2_.id AND (c2_.deletedAt IS NULL)
WHERE (u1_.organization_id = 7)                               |
|        2 | 0.04896325 | SELECT SQL_NO_CACHE COUNT(p0_.id) AS sclr0
FROM Purchase p0_
LEFT JOIN `User` u1_ ON p0_.user_id = u1_.id
LEFT JOIN Config c2_ ON u1_.id = c2_.id AND (c2_.deletedAt IS NULL)
WHERE (u1_.organization_id = 7)                             |
|        3 | 8.35424850 | SELECT SQL_NO_CACHE COUNT(p0_.id) AS sclr0  FROM Purchase p0_  LEFT JOIN `User` u1_ ON p0_.user_id = u1_.id  LEFT JOIN Config c2_ ON u1_.id = c2_.id AND (c2_.deletedAt IS NULL)  WHERE (u1_.organization_id = 7) AND (p0_.deletedAt IS NULL) |
+----------+------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)

AND (p0_.deletedAt IS NULL)INNER JOIN的查询大约需要同一时间: Prilfer output with INNER JOIN

以下是EXPLAIN的{​​{1}}输出:<{1}}:

EXPLAIN with AND

EXPLAIN without AND


表格结构。

采购:

AND

用户

CREATE TABLE `purchase` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `credit_card_user_id` int(11) DEFAULT NULL,
  `requestor_id` int(11) DEFAULT NULL,
  `organization_supplier_id` int(11) DEFAULT NULL,
  `organization_id` int(11) DEFAULT NULL,
  `deletedAt` datetime DEFAULT NULL,
  `status` int(11) NOT NULL,
  `number` int(11) NOT NULL,
  `created_at` datetime NOT NULL,
  `amount` double NOT NULL,
  `xml` longtext COLLATE utf8_unicode_ci NOT NULL,
  `comments` longtext COLLATE utf8_unicode_ci,
  `payload_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `error` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `shipping_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `shipping_email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `shipping_phone_name` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `shipping_phone` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `shipping_fax` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
  `shipping_deliver_to_1` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `shipping_deliver_to_2` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `is_payment_info_amount_percent` tinyint(1) DEFAULT NULL,
  `temporary_old_id` int(11) DEFAULT NULL,
  `user_address_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `number_organization` (`number`,`organization_id`),
  KEY `IDX_9861B36DA76ED395` (`user_id`),
  KEY `IDX_9861B36DD68143A9` (`credit_card_user_id`),
  KEY `IDX_9861B36DA7F43455` (`requestor_id`),
  KEY `IDX_9861B36D1A81C9F7` (`organization_supplier_id`),
  KEY `IDX_9861B36D32C8A3DE` (`organization_id`),
  KEY `IDX_9861B36D52D06999` (`user_address_id`),
  CONSTRAINT `FK_9861B36D1A81C9F7` FOREIGN KEY (`organization_supplier_id`) REFERENCES `organizationsupplier` (`id`),
  CONSTRAINT `FK_9861B36D32C8A3DE` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`),
  CONSTRAINT `FK_9861B36D52D06999` FOREIGN KEY (`user_address_id`) REFERENCES `useraddress` (`id`),
  CONSTRAINT `FK_9861B36DA76ED395` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  CONSTRAINT `FK_9861B36DA7F43455` FOREIGN KEY (`requestor_id`) REFERENCES `requestor` (`id`),
  CONSTRAINT `FK_9861B36DD68143A9` FOREIGN KEY (`credit_card_user_id`) REFERENCES `creditcarduser` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=359199 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

配置

CREATE TABLE `User` (
  `id` int(11) NOT NULL,
  `organization_id` int(11) DEFAULT NULL,
  `username` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `password` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
  `salt` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
  `roles` longtext COLLATE utf8_unicode_ci NOT NULL COMMENT '(DC2Type:json_array)',
  `firstname` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `lastname` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `middlename` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `is_enabled` tinyint(1) DEFAULT NULL,
  `login_attempts` int(11) NOT NULL,
  `employee_id` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` datetime DEFAULT NULL,
  `password_changed_at` datetime NOT NULL,
  `allow_all_suppliers` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_2DA1797732C8A3DE` (`organization_id`),
  CONSTRAINT `FK_2DA1797732C8A3DE` FOREIGN KEY (`organization_id`) REFERENCES `organization` (`id`),
  CONSTRAINT `FK_2DA17977BF396750` FOREIGN KEY (`id`) REFERENCES `config` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

1 个答案:

答案 0 :(得分:1)

对于带有AND子句的查询,MySQL必须查找实际购买记录以检索deletedAt值。根据{{​​1}}结果,每1968个用户记录发生35次。

如果没有EXPLAIN,MySQL可以使用AND索引来检索购买IDX_9861B36DA76ED395user_id(对于id),所以它不必查看任何其他信息的实际购买记录。

添加以下多列索引可能会改善这种情况:COUNT