我通常以成为数据库专家为荣,但我无法真正理解这种行为。我希望有人能解释这是如何运作的。
我有两个mysql表命令:
CREATE TABLE `orders` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`status` tinyint(4) NOT NULL,
`total` decimal(7,2) NOT NULL,
`date_created` datetime NOT NULL,
`date_updated` datetime NOT NULL,
`voucher_code` varchar(127) DEFAULT NULL,
`voucher_id` int(11) unsigned DEFAULT NULL,
`user_id` int(11) unsigned DEFAULT NULL,
`billing_address_id` int(11) unsigned NOT NULL,
`shipping_address_id` int(11) unsigned NOT NULL,
`reference_id` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `reference_id` (`reference_id`),
KEY `address_id` (`billing_address_id`)
) ENGINE=InnoDB AUTO_INCREMENT=168067 DEFAULT CHARSET=latin1;
和地址:
CREATE TABLE `addresses` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` tinyint(4) DEFAULT NULL,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`street` varchar(255) NOT NULL,
`street2` varchar(255) DEFAULT NULL,
`company_name` varchar(255) DEFAULT NULL,
`city` varchar(45) NOT NULL,
`postcode` varchar(45) DEFAULT NULL,
`region` varchar(45) DEFAULT NULL,
`country` varchar(45) NOT NULL,
`phone` varchar(45) DEFAULT NULL,
`user_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fk_addresses_users1_idx` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=95277 DEFAULT CHARSET=latin1;
现在您可以看到我已在订单表中为名为billing_address_id
的{{1}}创建了一个索引,该索引应与地址address_id
匹配。
这是我正在尝试运行的查询:
id
如果我在没有任何索引规范的情况下运行查询,它将拾取并使用address_id索引,我希望这是匹配这两个表的最快方法。
奇怪的是,' address_id'索引查询在2秒内运行。 如果我使用正常的主要原因'对订单ID起作用的索引需要0.000秒。
这让我烦恼。我以为我应该创建索引来加快表之间的连接过程。
如果我对两个查询运行EXPLAIN:
SELECT
o.id, a.first_name, a.last_name, o.total, o.date_created
FROM
orders o USE INDEX FOR JOIN (PRIMARY) JOIN
addresses a ON a.id = o.billing_address_id
ORDER BY id DESC
LIMIT 0, 50
使用索引:
EXPLAIN EXTENDED
SELECT o.id, a.first_name, a.last_name, o.total, o.date_created
FROM orders o
JOIN addresses a ON a.id = o.billing_address_id
ORDER BY id DESC
LIMIT 0, 50
1 SIMPLE a ALL PRIMARY 95234 100.00 Using temporary; Using filesort
1 SIMPLE o ref address_id address_id 4 my_basket.a.id 1 100.00
感谢您抽出时间回答这个问题。
答案 0 :(得分:0)
我很惊讶两个查询甚至编译 - ORDER BY id
都不明确,因为每个表都有不同的 id
。
执行JOIN
时,总是限定所有列。
同时,删除USE INDEX
。
答案 1 :(得分:0)
对于ORDER BY ... LIMIT查询,使用避免排序的查询执行计划通常是有益的。这不一定是因为排序很昂贵,但是因为一旦找到所请求的行数(这里是50)就可以停止查询执行。
在您的情况下,如果以表a开头,则必须在选择“顶部”50行之前生成完整的连接结果。如果从使用PRIMARY索引扫描表o开始,则联接结果将在o.id上排序,并且一旦找到50行,连接执行就会停止。
自MySQL 5.6以来,用于在两种方法之间进行选择的成本模型得到了改进。我建议你试试MySQL 5.7,看看MySQL优化器现在是否能够选择最优的计划。