Mysql 以奇怪的顺序进行连接;把订单搞砸了?
假设我有三个表——用户、服务器和付款。每个用户可以有多个服务器,每个服务器可以有多个付款。也就是说,我想查找最近的付款,并获取这些付款所附加到的服务器/客户的信息。下面是一个可以执行此操作的查询:Mysql 以奇怪的顺序进行连接;把订单搞砸了?,mysql,join,sql-order-by,query-optimization,explain,Mysql,Join,Sql Order By,Query Optimization,Explain,假设我有三个表——用户、服务器和付款。每个用户可以有多个服务器,每个服务器可以有多个付款。也就是说,我想查找最近的付款,并获取这些付款所附加到的服务器/客户的信息。下面是一个可以执行此操作的查询: SELECT * FROM payments p JOIN customers c ON p.custID = c.custID JOIN servers s ON s.serverID = p.serverID WHERE c.hold = 0 AND c.archive = 0 ORDER
SELECT *
FROM payments p
JOIN customers c ON p.custID = c.custID
JOIN servers s ON s.serverID = p.serverID
WHERE c.hold = 0
AND c.archive = 0
ORDER BY p.paymentID DESC
LIMIT 10;
问题是,当我对此查询运行EXPLAIN时,我得到以下结果:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE c ref PRIMARY,hold_archive hold_archive 3 const,const 28728 Using where; Using index; Using temporary; Using filesort
1 SIMPLE p ref custID custID 5 customers.custID 3 Using where
1 SIMPLE s eq_ref PRIMARY PRIMARY 4 payments.serverID 1 Using index
问题是查询需要一段时间才能运行。如果我取消订单,它会变为10倍快。但是我需要你的订单。以下是我删除订单时的解释:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE c ref PRIMARY,hold_archive hold_archive 3 const,const 28728 Using where; Using index
1 SIMPLE p ref custID custID 5 customers.custID 3 Using where
1 SIMPLE s eq_ref PRIMARY PRIMARY 4 payments.serverID 1 Using index
因此,这里最大的区别在于,额外列中缺少“使用临时”和“使用文件排序”
在本例中,原因似乎是,我正在执行ORDER BY的列不是解释中的第一列
另一个观察。如果我删除其中一个WHERE子句(同时保持顺序),它的速度将类似,但我需要两个WHERE。下面是一个例子来解释这一点:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE p index custID,serverID PRIMARY 4 NULL 10 Using where
1 SIMPLE c eq_ref PRIMARY,hold_archive PRIMARY 4 payments.custID 1 Using where
1 SIMPLE s eq_ref PRIMARY PRIMARY 4 payments.serverID 1 Using index
此处,在解释的第一列上按列/排序。但是为什么MySQL要重新安排表的连接顺序,我如何才能使它不这样做呢?您可以在MySQL中强制使用索引,但这似乎没有什么帮助
有什么想法吗?快10倍——它可以找到“任意10行”,比“找到所有可能的行,对它们进行排序,然后交付10行”快得多
让WHERE
和ORDER BY
命中不同的列很难优化
持有=0且存档=0的付款百分比是多少?这听起来像一个小百分比?每个表有多少行
还有什么需要索引(保存、存档)?如果没有,就把它扔掉。这似乎只是在这里制造麻烦
如果hold=0和archive=0
是常见的,那么您希望执行过程像第三次EXPLAIN
——即按降序扫描付款。由于它们中的大多数匹配,其中
,通常需要点击不超过10行才能找到10个匹配行
另一种解决方案(除去索引之外)是将查询中的JOIN
更改为stright\u JOIN
。这会告诉优化器您更清楚,应该首先扫描付款
,然后扫描客户
。如果我的前一段适用的话,这很有效
但是,如果您查找archive=1
这样的问题,那么查询会出错(速度慢),您还需要为所有相关表提供SHOW CREATE TABLE语句快速猜测:如果没有ORDER by
子句,然后,服务器返回符合条件的前10条记录,并在达到该数字后停止处理。它可能从customer
表开始,因为该表上有一个过滤器,然后查找相关的付款
,然后查找匹配的服务器。效果很好。但是,当您按
添加订单时,它首先需要列出所有匹配的记录(c
=>p
=>s
),对paymentID
上的记录进行排序,然后从该列表中从“最低”的记录开始选择前10个记录。