我在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;
输出的屏幕截图
以下是完全相同查询的输出,但没有AND (p0_.deletedAt IS NULL)
,请注意下面附加较新的查询(请参阅Query_ID = 5):
为了确保这不是缓存问题,我启动了另一个没有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
的查询大约需要同一时间:
以下是EXPLAIN
的{{1}}输出:<{1}}:
表格结构。
采购:
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
答案 0 :(得分:1)
对于带有AND
子句的查询,MySQL必须查找实际购买记录以检索deletedAt
值。根据{{1}}结果,每1968个用户记录发生35次。
如果没有EXPLAIN
,MySQL可以使用AND
索引来检索购买IDX_9861B36DA76ED395
和user_id
(对于id
),所以它不必查看任何其他信息的实际购买记录。
添加以下多列索引可能会改善这种情况:COUNT