【问题标题】:SQL joining three tables, join precedenceSQL连接三张表,连接优先级
【发布时间】:2010-09-23 15:56:49
【问题描述】:

我有三个表:R、S 和 P。

表 R 通过外键与 S 连接; 应该在 S 中至少有一条记录,所以我可以加入:

SELECT
        *
    FROM 
        R
    JOIN    S ON (S.id = R.fks)

如果 S 中没有记录,那么我没有得到任何行,那很好。

然后表 S 与 P 连接,其中记录为 P 可能存在也可能不存在并与 S 连接。

所以我愿意

SELECT
        *
    FROM 
        R
    JOIN    S ON (S.id = R.fks)
    LEFT JOIN P ON (P.id = S.fkp)

如果我希望将第二个 JOIN 绑定到 S 而不是 R 怎么办,就像我可以使用括号一样:

SELECT
        *
    FROM 
        R
    JOIN    (S ON (S.id = R.fks) JOIN P ON (P.id = S.fkp))

或者这已经是 R、S 和 P 之间的笛卡尔积的自然行为了吗?

【问题讨论】:

  • 您能否详细说明这部分“如果我希望将第二个 JOIN 绑定到 S 而不是 R”现在这些表作为 R 与 S 连接,然后与 P 左连接。跨度>
  • 我试图了解是否有一种方法可以优先考虑连接,就像它们是算术运算符一样。我不知道这是否隐含在连接条件中。
  • 给我们一个数据示例,说明您希望实现的与普通连接不同的目标,我们可以更好地向您展示该怎么做。派生表可能会执行您想要的操作,但我不确定您到底想要什么。
  • Oracle DB 几乎完全按照您的要求支持括号。
  • 继续@DavidBalažic 评论 - 这是相关文档:docs.oracle.com/cd/E17952_01/mysql-5.5-en/…

标签: sql


【解决方案1】:

所有类型的外部连接和普通连接都在同一个优先级中,并且运算符在查询的给定嵌套级别从左到右生效。您可以将连接表达式放在右侧的括号中,使其首先生效。请记住,您必须移动 ON 子句以便它们与它们的连接保持一致——括号中的连接将其 ON 子句与它一起放入括号中,因此它现在在文本上位于另一个 ON 子句之前将在外部联接语句中的括号之后。

(PostgreSQL 示例)

SELECT * FROM a LEFT JOIN b ON (a.id = b.id) JOIN c ON (b.ref = c.id);

a-b join先生效,但我们可以通过将b-c join放在括号中来强制b-c join先生效,如下所示:

SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);

通常你可以通过移动连接和改变外连接的方向来表达相同的东西而无需额外的括号,例如

SELECT * FROM b JOIN c ON (b.ref = c.id) RIGHT JOIN a ON (a.id = b.id);

【讨论】:

  • 澄清:所有连接运算符具有相同的优先级,并且都具有从左到右的关联性。这就是减法 (-) 和除法 (/) 运算符在几乎所有编程语言中的工作方式。更改操作数的顺序对其“优先级”没有影响(SQL 语法的一个方面,在任何情况下都不是操作数)。这样做利用了连接的从左到右的关联性,以实现复合连接的所需语义。但它既不会改变表达式中任何内容的优先级也不会改变关联性 - 只会改变其解释的上下文。
  • 很公平。什么是第二个词,它传达了一个运算符的效果在另一个运算符的效果之前,但不是优先级,因为已经采取了?
  • 改写了。此外,尽管我讨厌通过将某人的行话用作字典定义(不,我不这样做)来踩踏某人的行话,但真正让我感到厌烦的是诸如“关联性”之类的东西,其中行话是已被挪用的数学术语由计算世界。
  • 如果您正在寻找一个比优先级或关联性更具体的术语,我建议您使用“绑定”。这种用法既不太具体,也更非正式。运算符和操作数的绑定受运算符优先级和运算符关联性的影响。无论如何,应该意识到优先级和关联性是相关但非常不同的概念。
  • 回复:改写。我建议润滑你的齿轮以防止磨损。当准确性和清晰度如此容易获得时,没有 - 重复 NO - 选择模糊和模棱两可的理由。您在技术讨论中将定义明确、易于理解、广泛认可和相关的术语描述为“行话”,这至少表明对主题的态度不那么认真。运算符优先级根本不处理操作数顺序 - 只有运算符关联性才能处理。至于起源于数学的后期术语,优先级也是如此。
【解决方案2】:

当你加入第三张表时,你的第一个查询

SELECT
        *
    FROM 
        R
    JOIN    S ON (S.id = R.fks)

就像您要加入第三个表的派生表。因此,如果 R JOIN S 没有产生任何行,那么加入 P 将永远不会产生任何行(因为您正在尝试加入一个空表)。

所以,如果您正在寻找优先规则,那么在这种情况下,它只是使用 LEFT JOIN 而不是 JOIN 来设置的。

但是,我可能误解了您的问题,因为如果我正在编写查询,我会交换 SR。例如。

SELECT
        *
    FROM 
        S
    JOIN    R ON (S.id = R.fks)

【讨论】:

  • 考虑我必须加入 3 个表,而不是 2 个。
  • 它只是从左到右工作。我能想到的唯一解决方法是将整个派生表放在一些括号中。
【解决方案3】:

第二个连接绑定到 S,因为您明确声明 JOIN P ON (P.id = S.fkp) - 连接中没有引用来自 R 的列。

【讨论】:

  • 好的,但一般来说,没有办法优先考虑联接?
  • 我不认为 SQL 按照您的想法工作。 Paul Spangle 的回答描述了 SQL 如何“思考”连接。
【解决方案4】:
with a as (select 1 as test union select 2)
select * from a left join
a as b on a.test=b.test and b.test=1 inner join
a as c on b.test=c.test 
go
with a as (select 1 as test union select 2)
select * from a inner join
a as b on a.test=b.test  right join
a as c on b.test=c.test and b.test=1

理想情况下,我们希望上述两个查询是相同的。然而,它们不是——所以任何说在所有情况下都可以用左连接代替右连接的人是错误的。只有使用正确的join才能得到需要的结果。

【讨论】:

  • 右连接与这个问题无关,没有人说它们是一样的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-04
  • 1970-01-01
  • 2016-08-28
  • 2012-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多