Mysql 我可以指定连接发生的顺序吗?

Mysql 我可以指定连接发生的顺序吗?,mysql,join,left-join,inner-join,Mysql,Join,Left Join,Inner Join,假设我有三个表,A,B和C。概念上A(可选)有一个B,B(总是)有一个C Table A: a_id ... other stuff Table B: a_fk_id (foreign key to A.a_id, unique, primary, not null) c_fk_id (foreign key to C.c_id, not null) ... other stuff Table C: c_id ... other stuff 我想选择A中的所有记录,以及B和C中的相关记录(

假设我有三个表,A,B和C。概念上A(可选)有一个B,B(总是)有一个C

Table A:
a_id
... other stuff

Table B:
a_fk_id (foreign key to A.a_id, unique, primary, not null)
c_fk_id (foreign key to C.c_id, not null)
... other stuff

Table C:
c_id
... other stuff
我想选择A中的所有记录,以及B和C中的相关记录(如果存在)。但是,只有当B和C都存在时,B和C数据才能出现在结果中

我觉得我想做:

SELECT *
FROM
    A
    LEFT JOIN B on A.a_id=B.a_fk_id
    INNER JOIN C on B.c_fk_id=C.c_id
但是联接似乎是左关联的(第一个联接发生在第二个联接之前),因此这不会给出来自在C中没有条目的A的记录

AFAICT我必须使用子查询,大致如下:

SELECT *
FROM
    A
    LEFT JOIN (
        SELECT * FROM B INNER JOIN C ON B.c_fk_id=C.c_id
    ) as tmp ON A.id = tmp.a_fk_id
但是一旦我在一个查询中有两个这样的关系(实际上我可能有两个或三个嵌套关系),我就担心代码的复杂性和查询优化器

除了这个子查询方法之外,还有其他方法可以指定联接顺序吗


谢谢。

编辑:,这就是我说话比想象的快的原因。我以前的解决方案不起作用,因为“c”尚未加入。让我们再试一次

我们可以使用WHERE子句将结果限制为仅与您要查找的条件匹配的结果,其中C有一个有效值(不为NULL)或B没有一个值(为NULL)。像这样:

SELECT *
FROM a
    LEFT JOIN b ON (b.a = a.a)
    LEFT JOIN c ON (c.b = b.b)
WHERE (c.c IS NOT NULL OR b.b IS NULL);
如果没有结果:

mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b);
+------+------+------+------+------+
| a    | a    | b    | c    | b    |
+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |
|    1 |    1 |    2 | NULL | NULL |
|    2 |    2 |    3 |    2 |    3 |
|    3 | NULL | NULL | NULL | NULL |
|    4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b) WHERE (c.c IS NOT NULL OR b.b IS NULL);
+------+------+------+------+------+
| a    | a    | b    | c    | b    |
+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |
|    2 |    2 |    3 |    2 |    3 |
|    3 | NULL | NULL | NULL | NULL |
|    4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
结果如下:

mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b);
+------+------+------+------+------+
| a    | a    | b    | c    | b    |
+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |
|    1 |    1 |    2 | NULL | NULL |
|    2 |    2 |    3 |    2 |    3 |
|    3 | NULL | NULL | NULL | NULL |
|    4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b) WHERE (c.c IS NOT NULL OR b.b IS NULL);
+------+------+------+------+------+
| a    | a    | b    | c    | b    |
+------+------+------+------+------+
|    1 |    1 |    1 |    1 |    1 |
|    2 |    2 |    3 |    2 |    3 |
|    3 | NULL | NULL | NULL | NULL |
|    4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+

在SQL Server中,您可以执行以下操作

SELECT *
FROM   a
       LEFT JOIN b
                 INNER JOIN c
                   ON b.c_fk_id = c.c_id
         ON a.id = b.a_fk_id  

ON
子句的位置意味着
ON
b
上的
LEFT JOIN
逻辑上发生在最后。据我所知,这是标准的(),但我相信如果它在MySQL中不起作用的话,否决票会通知我

是的,您可以使用
直接连接进行此操作。
使用此关键字时,联接将按照指定的确切顺序进行


请参阅:

好吧,我还想出了另一个解决方案,为了完整起见,我发布了它(尽管我实际上使用了Martin的答案)

使用右连接:

SELECT 
    *
FROM
    b
    INNER JOIN c ON b.c_fk_id = c.c_id
    RIGHT JOIN a ON a.id = b.a_fk_id

我敢肯定,我读到的每一篇关于连接的文章都说正确的连接是毫无意义的,但事实就是如此。

令人惊讶的是,这是有效的。我用我为我的答案创建的表格对它进行了测试,结果与我的版本相同。这是我以前从未遇到过的语法,所以我的第一印象是其他人很难理解它,因为我对MySQL很在行,但一旦你知道发生了什么,也许就不会那么糟糕了。耸耸肩不确定,但我会检查它在MySQL 5.1.19上是否有效。MySQL从5.0.1()开始就支持嵌套连接。@bfavaretto-感谢链接。看起来这个答案可能实际上缺少了一些括号?@MartinSmith,我不确定,但我认为括号是可选的(或者至少在某些情况下不是必需的)。这真是该死的gud,但我不明白你的问题,不允许来自“A”的条目显示“C”是否没有条目。直联只是一种查询优化工具;它不会改变返回的结果。谢谢。和马丁一起去,因为它避开了一个地方。IIUC在连接之后使用WHERE,因此另一个解决方案可以更早地消除行,因此效率更高。也许是我在这里发自肺腑的话。如果您想知道哪个查询更有效,只需在它们前面加上一个。它将告诉你它将使用什么索引,以及从每个表中要考虑它的答案大约有多少行。假设是好的,但总是尝试去证实。我已经让MySQL在大型数据集上进行了几次奇怪的优化。