【问题标题】:Do I misunderstand joins?我误解了联接吗?
【发布时间】:2012-03-28 04:59:56
【问题描述】:

我正在尝试学习 ansi-92 SQL 标准,但我似乎并不完全理解它(我也是 ansi-89 标准以及一般数据库的新手)。

在我的示例中,我有三个表王国 -

  • 可能有没有物种或家庭的王国。
  • 可能有没有物种或种类的科。
  • 可能有些物种没有王国或家族。

为什么会发生这种情况?

假设生物学家发现了一个新物种,但他没有将其归类为王国或科,创建了一个没有物种并且不确定它应该属于哪个王国的新科,等等。

这是一个小提琴(见最后一个查询):http://sqlfiddle.com/#!4/015d1/3

我想做一个查询来检索每个王国,每个物种,但不是那些没有物种的家庭,所以我做了这个。

    select *
    from reino r
         left join (
             familia f             
             right join especie e
                 on f.fnombre = e.efamilia
                 and f.freino = e.ereino
         ) on r.rnombre = f.freino 
           and r.rnombre = e.ereino;

我认为这会做的是:

  1. 将家庭和物种作为右连接加入,因此它会带来所有物种,但不会带来没有物种的家庭。所以,如果一个物种没有被归入一个科,它会在科上显示为空。

  2. 然后,将结果作为左连接加入王国,因此它会带来每个王国,即使该王国中没有分类的科或物种。

我错了吗?这不应该告诉我那些没有分类的物种吗?如果我进行内部查询,它会带来我想要的。我对事物进行分组有问题吗?

【问题讨论】:

  • 这是否与 oracle 所做的查询优化相混淆?在我的查询中,括号是不必要的吗?我得到相同的结果吗? oracle 指令的顺序是什么?
  • @outis 我不得不恭敬地不同意连接语法不会嵌套。 I believe that this article discusses this topic 并得出结论,在连接多个表时,可以使用括号来阐明操作顺序。
  • @MichaelFredrickson:我的立场是正确的。就是这样,SQL/Foundation § 7.6。

标签: sql oracle outer-join


【解决方案1】:

您对 #1 的描述是正确的...您的查询问题出在步骤 #2。

当您从王国到(家庭和物种)执行left join 时,您正在请求每个王国,即使没有匹配的(家庭和物种)......但是,这不会返回任何(家庭& 物种)组合,没有匹配的王国。

更接近的查询是:

select *
    from reino r
         full join (
             familia f             
             right join especie e
                 on f.fnombre = e.efamilia
                 and f.freino = e.ereino
         ) on r.rnombre = f.freino 
           and r.rnombre = e.ereino;

请注意 left join 已替换为 full join...

但是,这只会返回与物种相关的家庭...它不会返回与王国相关但与物种无关的任何家庭。

重新阅读您的问题后,这实际上是您想要的......


编辑:进一步考虑,您可以像这样重写您的查询:

select *
from 
    especie e
    left join familia f 
        on f.fnombre = e.efamilia
        and f.freino = e.ereino
    full join reino r
        on r.rnombre = f.freino 
        and r.rnombre = e.ereino;

我认为这将是可取的,因为您消除了 RIGHT JOIN,通常因为风格不佳而不受欢迎......以及括号,人们很难正确解析以确定结果将是什么.

【讨论】:

  • 非常感谢,现在事情更清楚了。感谢您抽出宝贵时间:)
  • 不过还有一个问题;阅读outis对我的问题的评论。括号是不必要的吗?这会与 Oracle 优化器混淆吗?
  • 我不同意 @outis... 但我主要在 SQL Server 中工作,所以这在 Oracle 中可能会有所不同。但是,根据我的经验,括号会影响结果和执行计划。 Here's a source that I believe confirms this in Oracle.... 我不确定如何在不使用括号的情况下重组该查询(您可能必须将其更改为子选择),但如果可以,您可以比较执行计划两种方法来确保您的原始方法是可以接受的。
【解决方案2】:

如果这有帮助:

相对而言,[OUTER JOIN是]一种霰弹枪婚姻:它 强制表格进入一种联合——是的,我的意思是联合,而不是联合——甚至 当相关表格不符合通常要求时 为工会。它实际上是通过填充一个或 在进行联合之前,两个表都为空,从而使 毕竟它们符合那些通常的要求。但是没有 为什么不应该使用适当的值来完成填充的原因 空值,如本例所示:

SELECT SNO , PNO 
FROM   SP 
UNION  
SELECT SNO , 'nil' AS PNO 
FROM   S 
WHERE  SNO NOT IN ( SELECT SNO FROM SP )

以上等价于:

SELECT SNO , COALESCE ( PNO , 'nil' ) AS PNO 
FROM   S NATURAL LEFT OUTER JOIN SP

来源: SQL and Relational Theory: How to Write Accurate SQL Code By C. J. Date

【讨论】:

    【解决方案3】:

    如果您希望重写查询时只对现有查询稍作改动,您可以将LEFT 连接更改为FULL 连接。您可以进一步从ON 条件中删除多余的括号和r.rnombre = f.freino

    select *
    from reino r
         full join                      --- instead of LEFT JOIN
             familia f             
             right join especie e
                 on f.fnombre = e.efamilia
                 and f.freino = e.ereino
           on r.rnombre = e.ereino;
                                    ---removed the:    r.rnombre = f.freino    
    

    【讨论】:

    • 我想要最好的结果,我不在乎是否必须重新编写它;什么最适合数据库?为什么?
    • 由于您只需要有物种的家庭,因此不需要r.rnombre = f.freino。从 Join 结构中删除括号可能会或不会产生任何影响,具体取决于 DBMS(我不是 Oracle 专家,对此没有任何意见)。
    • 只是轻微的不。此查询(以及 Michael 接受的查询)将不包括拥有王国(但没有物种)的家庭。
    【解决方案4】:

    尝试使用这个:

    select * 
    from reino r 
    join especie e on (r.rnombre = e.ereino) 
    join familia f on (f.freino = e.ereino and f.fnombre = e.efamilia)
    

    有没有可能,你在餐桌上把 efamilia 和 enombre 互换了?

    【讨论】:

      猜你喜欢
      • 2011-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-26
      • 1970-01-01
      相关资源
      最近更新 更多