【问题标题】:How do you do many to many table outer joins?您如何进行多对多表外连接?
【发布时间】:2010-09-27 05:00:43
【问题描述】:

我有 3 张桌子,foo、foo2bar 和 bar。 foo2bar 是 foo 和 bar 之间的多对多映射。以下是内容。

select * from foo
+------+
| fid  |
+------+
|    1 |
|    2 |
|    3 |
|    4 |
+------+

select * from foo2bar
+------+------+
| fid  | bid  |
+------+------+
|    1 |    1 |
|    1 |    2 |
|    2 |    1 |
|    2 |    3 |
|    4 |    4 |
+------+------+

select * from bar
+------+-------+------+
| bid  | value | zid  |
+------+-------+------+
|    1 |     2 |   10 |
|    2 |     4 |   20 |
|    3 |     8 |   30 |
|    4 |    42 |   30 |
+------+-------+------+

我想要请求的是,“给我一份所有 fid 和 zid 为 30 的值的列表”

我希望得到所有 fid 的答案,因此结果如下所示:

+------+--------+
| fid  | value  |
+------+--------+
|    1 |   null |
|    2 |      8 |
|    3 |   null |
|    4 |     42 |
+------+--------+

【问题讨论】:

    标签: sql join many-to-many operator-precedence


    【解决方案1】:

    解决问题,您可以从您的选择开始。不要包含您最终不想看到的列。

    SELECT foo.fid, bar.value
    

    那我们就可以做WHERE子句了,可以看到和你说的一样。

    SELECT foo.fid, bar.value  
    WHERE bar.zid = 30
    

    现在棘手的部分是将 FROM 子句连接在一起,使用 LEFT JOIN,因为我们想查看每个 fid,无论是否有中间匹配:

    SELECT foo.fid, bar.value  
    FROM foo  
    LEFT JOIN foo2bar ON foo.fid = foo2bar.fid  
    LEFT JOIN bar ON foo2bar.bid = bar.bid  
    WHERE bar.zid = 30
    OR bar.zid IS NULL
    

    【讨论】:

    • 这不应该工作,因为它不会给出具有空 zid 的行(没有映射到条形行的 foo 行)
    【解决方案2】:

    FWIW,这个问题实际上并不是关于多对多:这可以很简单地作为一个联合来完成。

    SQL 中真正的多对多是CROSS JOIN

    【讨论】:

      【解决方案3】:
      SELECT * FROM
              foo LEFT JOIN
              (
              Foo2bar JOIN bar
                   ON foo2bar.bid = bar.bid AND zid = 30
              )
              ON foo.fid = foo2bar.fid;
      

      未经测试

      【讨论】:

      • 你有一个错字,但它确实有效。子查询对性能不利吗?
      • 与 Bill Karwin 的一样,它不是子查询
      【解决方案4】:

      如果您在 fid = 3 时没有返回一行,那么您的服务器已损坏。

      这段代码应该做我认为你想要的:

      SELECT
          F.fid,
          SQ.value
      FROM
          dbo.Foo F
      LEFT OUTER JOIN
           (
           SELECT F2B.fid, B.value
           FROM dbo.Bar B
           INNER JOIN dbo.Foo2Bar F2B ON F2B.bid = B.bid WHERE B.zid = 30
           ) SQ ON SQ.fid = F.fid
      

      请记住,如果 fid 与两个 zid 为 30 的柱相关,则可能会返回两个值。

      【讨论】:

      • 你是对的,我搞砸了我的例子,fid 3 行确实存在
      • 呃,你也是正确的,如果有两个 zid 为 30 的 bar 与同一个 foo 相关,就会出现问题。我的架构也没有阻止...
      【解决方案5】:
      SELECT * FROM foo
        LEFT OUTER JOIN (foo2bar JOIN bar ON (foo2bar.bid = bar.bid AND zid = 30))
        USING (fid);
      

      在 MySQL 5.0.51 上测试。

      这不是子查询,它只是使用括号来指定连接的优先级。

      【讨论】:

      • 实际上,括号是可选的,我相信 Bill 知道。
      • Bill,我认为如果 foo2bar 记录没有匹配的 bar 记录,这可能不起作用,因为它是内部连接?但无论如何我从不使用“ON”和“USING”,所以这是猜测。 :)
      • Parens 当然是可选的,但它们确实允许您覆盖默认优先级。就像在算术表达式中一样:10*10+10 等于 110,而 10*(10+10) 等于 200。
      • @le dorfier:OP 说 foo2bar 是多对多关系的交集表,因此 bar 中应该始终有一行与 foo2bar 中的给定行匹配。
      • C 程序员的肯定标志,假设单遍编译。 :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多