我刚刚处理了一个相当复杂的查询,运行时间为8秒。 EXPLAIN显示了一个奇怪的表顺序,即使使用FORCE INDEX提示也没有使用我的索引。我遇到了STRAIGHT_JOIN join关键字并开始用它替换我的一些INNER JOIN关键字。我注意到速度提升了很多。最后我用STRAIGHT_JOIN替换了所有我的INNER JOIN关键字用于此查询,它现在在.01秒内运行。
我的问题是你什么时候使用STRAIGHT_JOIN?什么时候使用INNER JOIN?如果您正在撰写好的查询,是否有任何理由不使用STRAIGHT_JOIN?
答案 0 :(得分:70)
我不建议在没有充分理由的情况下使用STRAIGHT_JOIN。我自己的经验是MySQL查询优化器选择一个比我想要的更糟糕的查询计划,但通常不足以绕过它,如果你总是使用STRAIGHT_JOIN就会这样做。
我的建议是将所有查询保留为常规JOIN。如果您发现一个查询正在使用次优查询计划,我建议首先尝试重写或重新构造查询,以查看优化程序是否会选择更好的查询计划。此外,至少对于innodb,请确保您的索引统计信息不仅仅是过时的(ANALYZE TABLE)。这可能导致优化器选择不良的查询计划。优化器提示通常应该是您的最后手段。
不使用查询提示的另一个原因是,随着表的增长,您的数据分布可能会随着时间的推移而发生变化,或者您的索引选择性可能会发生变化等。您的查询提示现在是最佳的,可能会随着时间的推移变得不理想。但是由于您现在过时的提示,优化器将无法调整查询计划。如果允许优化器做出决定,您将保持更灵活。
答案 1 :(得分:22)
“STRAIGHT_JOIN类似于JOIN,除了左表总是在右表之前读取。这可以用于连接优化器以错误顺序放置表的那些(少数)情况。”
答案 2 :(得分:18)
MySQL在复杂查询中选择连接顺序并不是必需的。通过将复杂查询指定为straight_join,查询按照指定的顺序执行连接。通过首先将表放在最小公分母并指定straight_join,您可以提高查询性能。
答案 3 :(得分:15)
这是最近才出现的情景。
考虑三个表,A,B,C。
A有3000行; B有300,000,000行;和C有2,000行。
定义外键:B(a_id),B(c_id)。
假设您有一个如下所示的查询:
select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id
根据我的经验,MySQL可能会选择去C - > B - >在这种情况下。 C小于A而B是巨大的,它们都是等同物。
问题是MySQL不一定要考虑(C.id和B.c_id)vs(A.id和B.a_id)之间交集的大小。如果B和C之间的连接返回与B一样多的行,那么这是一个非常糟糕的选择;如果从A开始将B过滤到与A一样多的行,那么它将是一个更好的选择。可以使用straight_join
来强制执行此命令:
select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id
现在必须在a
之前加入b
。
通常,您希望以最小化结果集中行数的顺序进行连接。因此,从一个小桌子开始加入,使得最终的连接也很小,是理想的选择。如果从一张小桌子开始,将它连接到一个更大的桌子,就像大桌子一样大,就会变成梨形。
这是统计数据依赖。如果数据分布发生变化,则计算可能会发生变化。它还取决于连接机制的实现细节。
我在MySQL看到的最糟糕的情况是,除了需要straight_join
或激进的索引提示之外,所有这些查询都是通过光过滤以严格排序顺序对大量数据进行分页。 MySQL强烈倾向于使用索引来对任何过滤器和连接进行排序;这是有道理的,因为大多数人并没有尝试对整个数据库进行排序,而是有一个响应查询的有限行的子集,并且排序有限的子集比过滤整个表要快得多,无论它是排序的还是不。在这种情况下,将直接连接放在具有索引列的表之后,我想对固定的事物进行排序。
答案 4 :(得分:11)
STRAIGHT_JOIN
,使用此子句,您可以控制JOIN
顺序:在外循环中扫描哪个表,哪个表在内循环中。
答案 5 :(得分:4)
我会告诉你为什么我必须使用STRAIGHT_JOIN:
因此,我强制其中一个连接为straight_join,以强制首先读取先前的连接。这阻止了MySQL改变执行顺序并像魅力一样工作!
答案 6 :(得分:1)
根据我的简短经验,STRAIGHT_JOIN
将查询从30秒减少到100毫秒的情况之一是执行计划中的第一个表不是按列排序的表
-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM
sales
INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id
LIMIT 50;
-- there is an index on (date, id)
如果优化程序选择首先点击stores
,则会导致Using index; Using temporary; Using filesort
,因为
如果ORDER BY或GROUP BY包含除表之外的表中的列 在连接队列中的第一个表中,创建了一个临时表。
这里优化器需要一点帮助,告诉他先使用
点击sales
sales STRAIGHT_JOIN stores
答案 7 :(得分:1)
如果您的查询以ORDER BY... LIMIT...
结尾,那么可能最适合重新构造查询,以欺骗优化程序在之前执行LIMIT
JOIN
。
(本答案不仅适用于STRAIGHT_JOIN
的原始问题,也不适用于STRAIGHT_JOIN
的所有案例。)
从example by @Accountantم开始,在大多数情况下运行速度会更快。 (并且它避免了需要提示。)
SELECT whatever
FROM ( SELECT id FROM sales
ORDER BY date, id
LIMIT 50
) AS x
JOIN sales ON sales.id = x.id
JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id;
注意:
INDEX(date, id)
。sales
让你只得到30" whatevers" 没有在临时表中牵引他们。ORDER BY
。我反对使用匹配,因为"即使它今天更快,明天也可能不会更快。"
答案 8 :(得分:0)
我知道它有些旧,但是这是一个场景,我一直在执行批处理脚本来填充特定表。在某个时候,查询运行非常缓慢。似乎在特定记录上的连接顺序不正确:
不正确的顺序运行约65秒,而使用Straight_join的运行时间以毫秒为单位
答案 9 :(得分:-4)
NgFor