MySQL JOIN的评估顺序是什么?

时间:2008-10-23 03:17:51

标签: mysql sql join

我有以下查询:

SELECT c.*
FROM companies AS c
JOIN users AS u USING(companyid)
JOIN jobs AS j USING(userid)
JOIN useraccounts AS us USING(userid)
WHERE j.jobid = 123;

我有以下问题:

  1. USING语法是否与ON语法同义?
  2. 这些联接是从左到右评估的吗?换句话说,这个查询是否说:x =公司加入用户; y = x JOIN工作; z = y JOIN useraccounts;
  3. 如果对问题2的回答是肯定的,那么假设公司表有companyid,userid和jobid列是否安全?
  4. 我不明白WHERE子句在引用别名“j”时如何用于在公司表上选择行
  5. 任何帮助将不胜感激!

7 个答案:

答案 0 :(得分:26)

  1. USING(fieldname)是在table1.fieldname = table2.fieldname上说的简写方式。

  2. SQL没有定义JOIN完成的“顺序”,因为它不是语言的本质。显然,必须在声明中指定一个订单,但INNER JOIN可以被认为是可交换的:您可以按任何顺序列出它们,您将获得相同的结果。

    那就是说,当构造一个SELECT ... JOIN,特别是包含LEFT JOIN的那个时,我发现将第三个JOIN视为将新表连接到第一个JOIN的结果,第四个JOIN是有意义的。加入第二个JOIN的结果,依此类推。

    更少见的是,指定的顺序会影响查询优化器的行为,因为它会影响启发式。

  3. 没有。组装查询的方式,它要求公司和用户都有一个companyid,job有一个userid和一个jobid,useraccounts有一个userid。但是,只有一个公司用户需要一个用户ID才能使JOIN工作。

  4. WHERE子句使用jobs表提供的列过滤整个结果 - 即所有JOINed列。

答案 1 :(得分:11)

我无法回答有关USING语法的问题。那真是怪了。我以前从未见过它,总是使用ON子句。

可以告诉你的是,JOIN操作的顺序是由查询优化器根据优化启发式系统构建其查询计划时动态确定的,其中一些是:

  1. 是否在主键字段上执行了JOIN?如果是这样,则在查询计划中获得高优先级。

  2. 是否在外键字段上执行了JOIN?这也是高优先级。

  3. 联接字段中是否存在索引?如果是这样,请优先考虑。

  4. 是否在WHERE子句中的字段上执行了JOIN操作?可以通过检查索引(而不是通过执行表扫描)来评估WHERE子句表达式吗?这是一个主要的优化机会,因此它会成为一个重要的优先事项。

  5. 联合专栏的基数是多少?具有高基数的列为优化器提供了更多机会来区分错误匹配(不满足WHERE子句或ON子句的那些),因此通常在低基数连接之前处理高基数连接。

  6. 连接表中有多少实际行?加入一个只有100个值的表将比创建一千万行的表加入更少的数据爆炸。

  7. 无论如何......关键是......有很多变量进入查询执行计划。如果您想了解MySQL如何优化其查询,请使用EXPLAIN语法。

    这是一篇很好的文章:

    http://www.informit.com/articles/article.aspx?p=377652


    ON EDIT:

    回答第4个问题:你不是在查询“公司”表。您正在查询FROM和USING子句中 ALL 四个表的联接交叉产品。

    “j.jobid”别名只是该连接表集合中其中一列的完全限定名称。

答案 2 :(得分:2)

在MySQL中,通过以下方式询问查询优化器通常会有什么兴趣:

EXPLAIN SELECT [...]

请参阅"7.2.1 Optimizing Queries with EXPLAIN"

答案 3 :(得分:0)

http://dev.mysql.com/doc/refman/5.0/en/join.html

并开始阅读:


加入MySQL 5.0.12中的处理更改

从MySQL 5.0.12开始,根据SQL:2003标准处理与USING的自然连接和连接,包括外连接变体。目标是根据SQL:2003将NATURAL的语法和语义与NATURAL JOIN和JOIN ... USING对齐。但是,联接处理中的这些更改可能会导致某些联接的输出列不同。此外,一些似乎在旧版本中正常工作的查询必须重写以符合标准。

这些变化有五个主要方面:

  • MySQL确定NATURAL或USING连接操作的结果列的方式(以及整个FROM子句的结果)。

  • 将SELECT *和SELECT tbl_name。*扩展为所选列的列表。

  • 解析NATURAL或USING联接中的列名。

  • 将NATURAL或USING连接转换为JOIN ... ON。

  • 在JOIN ... ON的ON条件下解析列名。

答案 4 :(得分:0)

我不确定ON和USING部分(虽然这个website说它们是相同的)

至于排序问题,它完全实现(可能是查询)具体。 MYSQL很可能在编译请求时选择订单。如果您确实想强制执行特定订单,则必须“嵌套”您的查询:

SELECT c.*
FROM companies AS c 
    JOIN (SELECT * FROM users AS u 
        JOIN (SELECT * FROM  jobs AS j USING(userid) 
              JOIN useraccounts AS us USING(userid) 
              WHERE j.jobid = 123)
    )

至于第4部分:where子句限制jobs表中的哪些行有资格加入。因此,如果由于匹配的用户标识而存在会加入但没有正确的jobid的行,那么它们将被省略。

答案 5 :(得分:0)

1)使用与on不完全相同,但它是简短的,其中两个表都有一个与你加入的名称相同的列...请参阅:http://www.java2s.com/Tutorial/MySQL/0100__Table-Join/ThekeywordUSINGcanbeusedasareplacementfortheONkeywordduringthetableJoins.htm

在我看来,阅读起来比较困难,所以我要拼出连接。

3)从这个查询中不清楚,但我猜它不会。

2)假设您正在加入其他表(并非直接在公司上),此查询中的订单确实很重要...请参阅下面的比较:

<强>原始

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u USING(companyid) 
    JOIN jobs AS j USING(userid) 
    JOIN useraccounts AS us USING(userid) 
WHERE j.jobid = 123

我认为这可能暗示:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u on u.companyid = c.companyid
    JOIN jobs AS j on j.userid = u.userid
    JOIN useraccounts AS us on us.userid = u.userid 
WHERE j.jobid = 123

你可以切换你加入职位的行&amp; usersaccounts在这里。

如果所有内容都加入公司会是什么样子:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u on u.companyid = c.companyid
    JOIN jobs AS j on j.userid = c.userid
    JOIN useraccounts AS us on us.userid = c.userid
WHERE j.jobid = 123

除非每个用户都有自己的公司,否则这在逻辑上并不合理。

4。)sql的神奇之处在于你只能显示某些列,但它们都是用于排序和过滤的列......

如果你回来了

SELECT c.*, j.jobid....  

您可以清楚地看到它正在过滤的内容,但是数据库服务器并不关心您是否输出了一行进行过滤。

答案 6 :(得分:0)

以下是JOIN优先顺序的更详细解答。在您的情况下,JOIN s都是可交换的。让我们尝试一下他们不在的地方。

构建架构:

CREATE TABLE users (
  name text
);

CREATE TABLE orders (
  order_id text,
  user_name text
);

CREATE TABLE shipments (
  order_id text,
  fulfiller text
);

添加数据:

INSERT INTO users VALUES ('Bob'), ('Mary');

INSERT INTO orders VALUES ('order1', 'Bob');

INSERT INTO shipments VALUES ('order1', 'Fulfilling Mary');

运行查询:

SELECT *
  FROM users
       LEFT OUTER JOIN orders
       ON orders.user_name = users.name
       JOIN shipments
       ON shipments.order_id = orders.order_id

结果:

仅返回Bob行

分析:

在此查询中,首先评估了LEFT OUTER JOIN,并对JOIN的合成结果评估了LEFT OUTER JOIN

第二次查询:

SELECT *
  FROM users
       LEFT OUTER JOIN (
         orders
         JOIN shipments
         ON shipments.order_id = orders.order_id)
         ON orders.user_name = users.name

结果:

Bob的一行(带有履行数据)和Mary的一行,履行数据为NULL。

分析:

括号改变了评估顺序。

进一步的MySQL文档位于https://dev.mysql.com/doc/refman/5.5/en/nested-join-optimization.html