【发布时间】:2012-11-01 14:35:17
【问题描述】:
这是给你的一个简洁的(显然是 MySQL):
# 设置东西 如果存在则删除数据库 index_test_gutza; 创建数据库 index_test_gutza; 使用 index_test_gutza; 创建表客户订单( id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, 发票 MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, 主键(id) ); 插入到 customer_order (身份证,发票) 价值观 (1, 1), (2, 2), (3, 3), (4, 4), (5, 5); 创建表 customer_invoice ( id MEDIUMINT UNSIGNED NOT NULL AUTO_INCREMENT, invoice_no MEDIUMINT UNSIGNED DEFAULT NULL, invoice_pdf LONGBLOB, 主键(id) ); 插入到 customer_invoice (id, invoice_no) 价值观 (1, 1), (2, 2), (3, 3), (4, 4), (5, 5); # 好的,这是牛肉 解释 选择 co.id FROM customer_order AS co; 解释 选择 co.id FROM customer_order AS co 按 co.id 订购; 解释 选择 co.id,ci.invoice_no FROM customer_order AS co 左加入 customer_invoice AS ci ON ci.id=co.invoice; 解释 选择 co.id,ci.invoice_no FROM customer_order AS co 左加入 customer_invoice AS ci ON ci.id=co.invoice 按 co.id 订购;底部有四个 EXPLAIN 语句。前两个结果完全符合您的预期:
+----+-------------+--------+-------+-------------- -+---------+---------+------+------+-----------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+-------+-------------- -+---------+---------+------+------+-----------+ | 1 |简单 |合作 |索引 |空 |初级 | 3 |空 | 5 |使用索引 | +----+-------------+--------+-------+-------------- -+---------+---------+------+------+-----------+第三个已经很有趣了——注意 customer_order 中的主键不再使用了:
+----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+--------------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+--------------+ | 1 |简单 |合作 |全部 |空 |空 |空 |空 | 5 | | | 1 |简单 |词 | eq_ref |初级 |初级 | 3 | index_test_gutza.co.invoice | 1 |使用索引 | +----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+--------------+然而,第四个是 zinger - 只需在主键上添加 ORDER BY 就会导致在 customer_order 上进行文件排序(这是意料之中的,因为上面已经被弄糊涂了) :
+----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+----------------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+----------------+ | 1 |简单 |合作 |全部 |空 |空 |空 |空 | 5 |使用文件排序 | | 1 |简单 |词 | eq_ref |初级 |初级 | 3 | index_test_gutza.co.invoice | 1 |使用索引 | +----+-------------+--------+--------+------------- --+---------+---------+-------------- --+--------+----------------+文件排序!虽然我从不使用 customer_order 表中的主键进行订购,以及 customer_invoice 表中的主键用于 JOIN。那么,以所有好的和正确的名义,为什么它会突然切换到文件排序?!更重要的是,我该如何避免这种情况?作为记录,我很乐意接受一份书面回答,解释为什么无法避免这种情况(如果是这样的话)。
正如您现在可能怀疑的那样,这实际上是在生产中发生的,虽然这些表绝不是巨大的(只有数百条记录),但 invoice 表(包含 PDF 文件)上的文件排序正在杀死服务器当我运行与上述类似的查询时(我需要这些查询以了解哪些订单已开具发票,哪些未开具发票)。
在你问之前,我设计了数据库,我认为我可以安全地将 PDF 文件存储在该表中,因为我从不需要任何搜索查询——我总是有它的主键就在眼前!
更新(cmets 概要)
以下是以下 cmets 中建议的概要,因此您不必阅读所有内容:
- *您应该在 customer_order.invoice* 上添加一个键——我实际上在生产中尝试过,它没有任何区别(因为它不应该)
-
你应该使用
USE INDEX——试过了,没用。我也试过FORCE INDEX——也没有结果(没有任何变化) -
您过度简化了用例,我们需要实际的生产查询——我可能在第一次迭代中剥离了太多,所以我更新了它(我刚刚在 @987654324 中添加了
, ci.invoice_no@ 用于最后几个查询)。作为记录,如果有人真的很好奇,这里是生产查询,原样(检索订单的最后一页):
上面的查询(同样,这正是我在生产中运行的)大约需要 14 秒才能运行。这是在生产环境中执行的简化查询,如上面的用例所示:
选择 corder.id, invoice.invoice_no 从 绳索 LEFT JOIN invoice ON invoice.id=corder.invoice 订购方式 corder.id DESC 限制 400, 20;这个需要 13 秒才能运行。请注意,LIMIT 没有任何区别只要我们谈论的是结果的最后一页(我们是)。也就是说,当涉及到文件排序时,检索最后 12 个结果或全部 412 个结果之间绝对没有显着差异。
结论
ypercube 的答案不仅正确,而且不幸的是它似乎是唯一合法的答案。我试图进一步将条件与字段分开,因为 SELECT * FROM corder 子查询最终可能涉及大量数据,如果 corder 本身包含 LONGBLOB(并且从子查询中的主查询复制字段是不优雅的),但不幸的是它没有t 似乎工作:
此操作失败,并显示以下错误消息:
错误 1235 (42000): 这个版本的 MySQL 还不支持 'LIMIT & IN/ALL/ANY/SOME 子查询'我使用的是 MySQL 5.1.61,它在 5.1 系列中相当新(显然 5.5.x 也不支持此功能)。
【问题讨论】:
-
您在连接中使用的
customer_order (invoice)上没有索引吗?我的意思是,在生产系统中。使用 5 行表进行测试几乎没有价值。执行计划可能与实际不同(有很多行)。 -
当我试图理解这一点时,我实际上尝试添加它,但它没有任何区别(如果你考虑一下,这是可以预料的)。是的,我在生产中这样做了——没有区别。
-
第二,表是MyISAM还是InnoDB?有区别。
-
最后,您的最后一个(有问题的)查询毫无用处。您(左)加入
customer_invoice,但您不使用它作为选择。如果您发布有用的查询会更好。否则,为什么要使用仅是第二个的第 4 个(但可能有很多重复的行)? -
MyISAM。如果您愿意,您可以在该查询中添加花里胡哨,这不会有任何区别(我显然选择的不仅仅是订单 ID)。
标签: mysql indexing sql-order-by left-join