何时使用STRAIGHT_JOIN与MySQL

时间:2009-02-04 17:07:14

标签: mysql join

我刚刚处理了一个相当复杂的查询,运行时间为8秒。 EXPLAIN显示了一个奇怪的表顺序,即使使用FORCE INDEX提示也没有使用我的索引。我遇到了STRAIGHT_JOIN join关键字并开始用它替换我的一些INNER JOIN关键字。我注意到速度提升了很多。最后我用STRAIGHT_JOIN替换了所有我的INNER JOIN关键字用于此查询,它现在在.01秒内运行。

我的问题是你什么时候使用STRAIGHT_JOIN?什么时候使用INNER JOIN?如果您正在撰写好的查询,是否有任何理由不使用STRAIGHT_JOIN?

10 个答案:

答案 0 :(得分:70)

我不建议在没有充分理由的情况下使用STRAIGHT_JOIN。我自己的经验是MySQL查询优化器选择一个比我想要的更糟糕的查询计划,但通常不足以绕过它,如果你总是使用STRAIGHT_JOIN就会这样做。

我的建议是将所有查询保留为常规JOIN。如果您发现一个查询正在使用次优查询计划,我建议首先尝试重写或重新构造查询,以查看优化程序是否会选择更好的查询计划。此外,至少对于innodb,请确保您的索引统计信息不仅仅是过时的(ANALYZE TABLE)。这可能导致优化器选择不良的查询计划。优化器提示通常应该是您的最后手段。

不使用查询提示的另一个原因是,随着表的增长,您的数据分布可能会随着时间的推移而发生变化,或者您的索引选择性可能会发生变化等。您的查询提示现在是最佳的,可能会随着时间的推移变得不理想。但是由于您现在过时的提示,优化器将无法调整查询计划。如果允许优化器做出决定,您将保持更灵活。

答案 1 :(得分:22)

来自MySQL JOIN reference

“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:

  • 我的查询存在性能问题。
  • 简化查询,查询突然提高效率
  • 试图弄清楚哪个具体部分带来了这个问题,我只是无法做到。 (2个左连接在一起很慢,每个连接都很快)
  • 然后我使用慢速和快速查询执行EXPLAIN(addind其中一个左连接)
  • 令人惊讶的是,MySQL完全改变了2个查询之间的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包含除表之外的表中的列   在连接队列中的第一个表中,创建了一个临时表。

source

这里优化器需要一点帮助,告诉他先使用

点击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;

注意:

  • 首先,提取50个ID。 INDEX(date, id)
  • 会特别快
  • 然后返回sales让你只得到30" whatevers" 没有在临时表中牵引他们。
  • 由于子查询根据定义是无序的,因此必须重复ORDER BY
  • 是的,它更加混乱。但通常会更快。

我反对使用匹配,因为"即使它今天更快,明天也可能不会更快。"

答案 8 :(得分:0)

我知道它有些旧,但是这是一个场景,我一直在执行批处理脚本来填充特定表。在某个时候,查询运行非常缓慢。似乎在特定记录上的连接顺序不正确:

  • 顺序正确

enter image description here

  • 将id递增1会弄乱顺序。注意“额外”字段

enter image description here

  • 使用straight_join解决了该问题

enter image description here

不正确的顺序运行约65秒,而使用Straight_join的运行时间以毫秒为单位

答案 9 :(得分:-4)

NgFor