Bog*_*scu 5 mysql indexing sql-order-by left-join
这是给你的一个简洁的(显然是 MySQL):
# 设置
如果存在则删除数据库index_test_gutza;
创建数据库index_test_gutza;
使用index_test_gutza;
创建表 customer_order (
id 中型无符号非空自动递增,
发票 MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
主键(id)
);
插入客户订单
(身份证、发票)
价值观
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5);
创建表 customer_invoice (
id 中型无符号非空自动递增,
发票号 MEDIUMINT UNSIGNED DEFAULT NULL,
发票_pdf LONGBLOB,
主键(id)
);
插入客户发票
(id,发票号)
价值观
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5);
# 好的,这是牛肉
解释
选择 co.id
来自 customer_order AS co;
解释
选择 co.id
来自客户_订单 AS co
按 co.id 订购;
解释
选择 co.id、ci.invoice_no
来自客户_订单 AS co
LEFT JOIN customer_invoice AS ci ON ci.id=co.invoice;
解释
选择 co.id、ci.invoice_no
来自客户_订单 AS co
LEFT JOIN 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 | 使用索引 | +----+-------------+--------+--------+---------------- --+---------+---------+---------------------------- --+------+-------------+
然而,第四个是令人惊奇的——简单地在主键上添加 ORDER BY就会导致对 customer_order 进行文件排序(这是预料之中的,因为上面已经令人困惑了):
+----+-------------+--------+--------+---------------- --+---------+---------+---------------------------- --+------+----------------+ | 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键| key_len | 参考| 行 | 额外 | +----+-------------+--------+--------+---------------- --+---------+---------+---------------------------- --+------+----------------+ | 1 | 简单| 合作| 全部 | 空| 空| 空| 空| 5 | 使用文件排序| | 1 | 简单| 词 | eq_ref | 小学 | 小学 | 3 | index_test_gutza.co.invoice | 1 | 使用索引 | +----+-------------+--------+--------+---------------- --+---------+---------+---------------------------- --+------+----------------+
文件排序!而且,除了 customer_order 表中用于排序的主键和 customer_invoice 表中用于 JOIN 的主键之外,我从未使用过任何其他内容。那么,以一切美好和正确的名义,为什么它突然切换到文件排序呢?!更重要的是,我该如何避免这种情况?作为记录,我很乐意接受一个记录在案的答案,解释为什么这是无法避免的(如果是这样的话。)
正如您现在可能怀疑的那样,这实际上发生在生产中,尽管表并不大(只有数百条记录),但当我运行时,发票表(包含 PDF 文件)上的文件排序正在杀死服务器类似于上面的查询(我需要这些查询才能知道哪些订单已开具发票,哪些订单没有)。
在你问之前,我设计了数据库,并且我认为我可以安全地将 PDF 文件存储在该表中,因为我从来不需要对其进行任何搜索查询 - 我手边总是有它的主键!
以下是下面评论中建议内容的概要,因此您不必阅读所有内容:
USE INDEX- 尝试过,没有用。我也尝试过FORCE INDEX——也没有结果(没有任何改变), ci.invoice_no在SELECT最后几个查询中添加了它)。作为记录,如果有人真的很好奇,这里是生产查询,完全按照原样(这会检索订单的最后一页):选择
编码器.id,
corder.public_id,
CONCAT(买家.fname," ",买家.lname) AS 买家名称,
线.状态,
编码支付,
corder.保留AS R,
corder.tracking_id!="" 为 A,
corder. payment_received 作为 pay_date,
发票.invoice_no AS inv,
发票.receipt_no AS 记录,
发票.public AS pub_inv,
proforma.proforma_no AS 教授,
形式.public AS pub_pf,
评级,
corder. rating_comments!="" AS got_comment
从
科德尔
LEFT JOIN 用户作为买家 ON Buyer.id=corder.buyer
LEFT JOIN 发票作为发票 ONinvoice.id=corder.invoice
LEFT JOIN 发票作为形式 ON proforma.id=corder.proforma
订购依据
编号降序
限制 400、20;
上面的查询(同样,这正是我在生产中运行的查询)大约需要 14 秒才能运行。这是在生产中执行的简化查询,如上面的用例所示:
选择
编码器.id,
发票.invoice_no
从
科德尔
LEFT JOIN 发票 ONinvoice.id=corder.invoice
订购依据
corder.id DESC
限制 400、20;
这个运行需要 13 秒。请注意,只要我们谈论结果的最后一页(我们就是),LIMIT 就没有任何区别。也就是说,当涉及文件排序时,检索最后 12 个结果或所有 412 个结果之间绝对没有显着差异。
ypercube 的答案不仅是正确的,而且不幸的是它似乎是唯一合法的答案。我尝试进一步将条件与字段分开,因为SELECT * FROM corder如果 corder 本身包含 LONGBLOB,则子查询最终可能会涉及大量数据(并且在子查询中复制主查询中的字段是不优雅的),但不幸的是,它似乎并不工作:
选择
编码器.id,
corder.public_id,
CONCAT(买家.fname," ",买家.lname) AS 买家名称,
线.状态,
编码支付,
corder.保留AS R,
corder.tracking_id != "" AS A,
corder. payment_received AS pay_date,
发票.invoice_no AS inv,
发票.receipt_no AS 记录,
发票.public AS pub_inv,
proforma.proforma_no AS 教授,
形式.public AS pub_pf,
评级,
corder. rating_comments!="" AS got_comment
从
科德尔
LEFT JOIN 用户作为买家 ON Buyer.id = corder.buyer
LEFT JOIN 发票 AS 发票 ONinvoice.id = corder.invoice
LEFT JOIN 发票 AS 形式 ON proforma.id = corder.proforma
WHERE corder.id IN (
选择 ID
来自绳索
按 ID 描述排序
限制 400,20
)
订购依据
corder.id DESC;
此操作失败,并显示以下错误消息:
ERROR 1235 (42000):此版本的 MySQL 尚不支持“LIMIT & IN/ALL/ANY/SOME 子查询”
我使用的是 MySQL 5.1.61,它是 5.1 系列中最新的版本(显然 5.5.x 也不支持它)。
你能尝试这个版本吗(它基本上首先获取表的 420 行corder,保留其中的 20 行,然后执行 3 个外连接):
SELECT
corder.id,
corder.public_id,
CONCAT(buyer.fname," ",buyer.lname) AS buyer_name,
corder.status,
corder.payment,
corder.reserved AS R,
corder.tracking_id != "" AS A,
corder.payment_received AS pay_date,
invoice.invoice_no AS inv,
invoice.receipt_no AS rec,
invoice.public AS pub_inv,
proforma.proforma_no AS prof,
proforma.public AS pub_pf,
corder.rating,
corder.rating_comments!="" AS got_comment
FROM
( SELECT *
FROM corder
ORDER BY
id DESC
LIMIT 400, 20
)
AS corder
LEFT JOIN user as buyer ON buyer.id = corder.buyer
LEFT JOIN invoice AS invoice ON invoice.id = corder.invoice
LEFT JOIN invoice AS proforma ON proforma.id = corder.proforma
ORDER BY
corder.id DESC ;
Run Code Online (Sandbox Code Playgroud)