2008-10-23 194 views
21

我有以下查询:按照什么顺序评估MySQL 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. 的是利用语法的代名词。语法?
  2. 这些连接从左到右进行评估吗?换句话说,这个查询是否说:x =公司JOIN用户; y = x JOIN作业; z = y JOIN useraccounts;
  3. 如果问题2的答案是肯定的,那么假设公司表中有companyid,userid和jobid列是否安全?
  4. 我不明白的WHERE子句如何被用来挑公司的表行,当它指的是别名“J”

任何帮助,将不胜感激!

回答

23
  1. USING(fieldname)是一个简短的说ON ON table1.fieldname = table2.fieldname的方式。

  2. SQL没有定义JOINS完成的'顺序',因为它不是该语言的本质。很明显,在声明中必须指定一个命令,但是INNER JOIN可以被认为是可交换的:您可以按任意顺序列出它们,并且您将得到相同的结果。

    这就是说,当构建一个SELECT ... JOIN,尤其是包含LEFT JOIN的一个时,我发现将第三个JOIN作为第一个JOIN的结果加入新表是有意义的,第四个加入第二个JOIN的结果,等等。

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

  3. 否。查询的组装方式,它要求公司和用户都有一个companyid,jobs有一个userid和一个jobid,useraccounts有一个userid。但是,只有公司中的一个用户需要用于JOIN工作的用户标识。

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

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 JOIN和JOIN ... USING调整MySQL的语法和语义。但是,连接处理中的这些更改可能会导致某些连接的不同输出列。此外,一些似乎在旧版本中正常工作的查询必须重写以符合标准。

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

  • ,MySQL的确定的自然科学的结果列或使用连接操作(并因此整个FROM子句的结果)的方式。

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

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

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

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

9

我无法回答关于USING语法的问题。这很奇怪。我从来没有见过它,而是始终使用ON子句。

但我可以告诉你的是,JOIN操作的顺序是由查询优化器动态确定时,它构造查询计划,以优化启发式,其中有些是在系统上:

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

  2. JOIN是否在外键字段上执行?这也是重中之重。

  3. 连接字段上是否存在索引?如果是这样,请优先处理。

  4. 对WHERE子句中的字段执行JOIN操作吗?可以通过检查索引来评估WHERE子句表达式(而不是执行表扫描)吗?这是一个主要优化机会,所以它获得了一个主要优先级凹凸。

  5. 加入列的基数是多少?具有高基数的列使优化程序有更多机会区分错误匹配(不符合WHERE子句或ON子句的匹配项),因此通常会在低基数连接之前处理高基数连接。

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

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

这里是一个很好的文章阅读:

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


上编辑:

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

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

+0

,这真是个跨产品?我认为 SELECT * FROM table_a JOIN table_b USING(common_column) 会产生table_a中所有行,它们在table_b的common_column字段的任何行上都有匹配吗?这可能少于n行。 交叉乘积不会返回多少行? – 2008-10-23 18:05:41

+0

对不起。我对USING语法一无所知,所以我不能评论它是如何工作的。 “交叉产品”的评论只是参考一般的联接,它们有能力创建元组的组合爆炸,这就是优化器考虑基数的原因。 – benjismith 2008-10-24 17:13:40

0

林不知道有关ON VS使用部分(尽管这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子句限制什么从工作表中的行有资格被加入了。所以如果由于匹配用户标识符而有些行会加入,但没有正确的jobid,那么它们将被省略。

0

1)使用不完全一样,但它是手短两个表与您要加入的相同名称的列...看到:http://www.java2s.com/Tutorial/MySQL/0100__Table-Join/ThekeywordUSINGcanbeusedasareplacementfortheONkeywordduringthetableJoins.htm

更难以阅读在我看来,所以我会拼出联接。

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

2)假设你是通过其他表上companyies连接(不是全部直接)在此查询的顺序并不重要...见下面的比较:

原始:

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 

您合作您可以切换您的线路加入作业& usersaccounts here。

它会是什么样子,如果一切都加入了对公司:

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.... 

,你可以清楚地看到它过滤的是什么,但数据库服务器不关心你是否输出一行或不输入。

0

这里是JOIN优先级更详细的解答。在你的情况下,JOIN都是可交换的。让我们尝试一个他们没有的地方。

构建模式:

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 

结果:

只有鲍勃行返回

分析:

在这个查询中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 

结果:

鲍勃

一行(与履行数据)和一行玛利亚与空值的履行数据。

分析:

括号改变了评估顺序。


而且MySQL文档是https://dev.mysql.com/doc/refman/5.5/en/nested-join-optimization.html