【问题标题】:Ambiguity in Left joins (oracle only?)左连接中的歧义(仅限 Oracle?)
【发布时间】:2010-09-08 16:47:26
【问题描述】:

我的老板在我创建的查询中发现了一个错误,我不明白该错误背后的原因,尽管查询结果证明他是正确的。这是修复前的查询(简化版):

select PTNO,PTNM,CATCD
from PARTS 
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD); 

这是修复后的:

select PTNO,PTNM,PARTS.CATCD
from PARTS 
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD); 

错误是,CATCD 列显示空值,即查询结果包括表 CATEGORIES 而不是 PARTS 的结果。 这是我不明白的:如果原始查询中存在歧义,为什么 Oracle 不抛出错误?据我了解,在左连接的情况下,查询(PARTS)中的“主”表在歧义方面具有优先权。 是我错了,还是只是没有正确考虑这个问题?

更新:

这是一个修改后的例子,没有抛出歧义错误:

CREATE TABLE PARTS (PTNO NUMBER, CATCD NUMBER, SECCD NUMBER);

CREATE TABLE CATEGORIES(CATCD NUMBER);

CREATE TABLE SECTIONS(SECCD NUMBER, CATCD NUMBER);


select PTNO,CATCD 
from PARTS 
left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD) 
left join SECTIONS on (SECTIONS.SECCD=PARTS.SECCD) ;

有人知道吗?

【问题讨论】:

    标签: sql oracle join


    【解决方案1】:

    无论如何,通常建议具体并完全限定所有列名,因为它可以为优化器节省一些工作。当然在 SQL Server 中。

    从我可以从Oracle docs 中收集到的信息,似乎只有在您在选择列表中选择列名两次,或者在选择列表中选择一次,然后在其他地方(如 order by 子句)再次选择时,它才会抛出。

    也许您发现了一个“未记录的功能”:)

    【讨论】:

      【解决方案2】:

      对引发错误的 SQL 服务器感兴趣(应该如此)

      select id
      from sysobjects s
      left join syscolumns c on s.id = c.id
      

      服务器:消息 209,级别 16,状态 1,行 1 不明确的列名“id”。

      select id
      from sysobjects 
      left join syscolumns  on sysobjects.id = syscolumns.id
      

      服务器:消息 209,级别 16,状态 1,行 1 不明确的列名“id”。

      【讨论】:

        【解决方案3】:

        这是查询(简化版)

        我认为通过简化查询,您消除了错误的真正原因 :-)

        你用的是什么oracle版本? Oracle 10g ( 10.2.0.1.0 ) 给出:

        create table parts (ptno number , ptnm number , catcd number);  
        create table CATEGORIES (catcd number);
        
        select PTNO,PTNM,CATCD from PARTS  
        left join CATEGORIES on (CATEGORIES.CATCD=PARTS.CATCD);
        

        我得到 ORA-00918:列定义不明确

        【讨论】:

          【解决方案4】:

          我使用的是 Oracle 9.2.0.8.0。它确实给出了错误“ORA-00918: column ambiguously defined”。

          【讨论】:

            【解决方案5】:

            与 HollyStyles 一样,我在 Oracle 文档中找不到任何可以解释您所看到内容的内容。

            PostgreSQL、DB2、MySQL 和 MSSQL 都拒绝运行第一个查询,因为它不明确。

            【讨论】:

              【解决方案6】:

              根据我的经验,如果您创建这样的查询,当存在这样的字段重叠时,数据结果将从联接的右侧而不是左侧拉出 CATCD。

              因此,由于此连接将包含来自 PARTS 的所有记录,而只有一些来自 CATEGORIES 的记录,因此只要右侧没有数据,CATCD 字段中就会有 NULL。

              通过将列显式定义为来自 PARTS(即左侧),假设该字段在 PARTS 中有数据,您将获得一个非空值。

              请记住,使用 LEFT JOIN 时,您只能保证左侧表中的字段中的数据,右侧可能有空列。

              【讨论】:

              • 我理解你的回答。但是为什么我一开始没有得到一个模棱两可的错误呢?
              • 我不确定为什么。这是你的问题唯一奇怪的地方。
              【解决方案7】:

              @Pat:对于您的查询,我在这里遇到了同样的错误。我的查询比我最初发布的要复杂一点。我现在正在研究一个可重现的简单示例。

              【讨论】:

                【解决方案8】:

                恐怕我不能告诉你为什么你没有得到一个例外,但我可以假设为什么它选择了 CATEGORIES 的列版本而不是 PARTS 的版本。

                据我了解,在左连接的情况下,查询(PARTS)中的“主”表在歧义方面具有优先权

                不清楚“主”是指左连接中的左表,还是“驱动”表,正如您从概念上看到的查询......但在任何一种情况下,您所看到的“主” " 您编写的查询中的表不一定是该查询实际执行中的“主”表。

                我的猜测是,Oracle 只是使用它在执行查询时遇到的第一个表中的列。并且由于 SQL 中的大多数单独操作不需要先命中一个表,因此 DBMS 将在解析时决定首先扫描哪个最有效。尝试获取查询的执行计划。我怀疑它可能会显示它先击中 CATEGORIES,然后再击 PARTS。

                【讨论】:

                  【解决方案9】:

                  这可能是 Oracle 优化器中的一个错误。我可以使用 3 个表在查询中重现相同的行为。直觉上,它似乎确实应该产生一个错误。如果我用以下任何一种方式重写它,它确实会产生错误:

                  (1) 使用旧式外连接

                  select ptno, catcd
                  from parts, categories, sections
                  where categories.catcd (+) = parts.catcd
                    and sections.seccd (+) = parts.seccd
                  

                  (2) 显式隔离两个连接

                  select ptno, catcd
                  from (
                    select ptno, seccd, catcd
                    from parts
                    left join categories on (categories.CATCD=parts.CATCD) 
                  )
                  left join sections on (sections.SECCD=parts.SECCD)
                  

                  我使用 DBMS_XPLAN 来获取有关查询执行的详细信息,这确实显示了一些有趣的东西。该计划基本上是外连接 PARTS 和 CATEGORIES,投影该结果集,然后将其外连接到 SECTIONS。有趣的是,在第一个外连接的投影中,它只包括 PTNO 和 SECCD——它不包括前两个表中的任何一个的 CATCD。因此最终的结果是从第三个表中得到CATCD。

                  但我不知道这是原因还是结果。

                  【讨论】:

                    【解决方案10】:

                    您应该问自己一个更大的问题是 - 为什么我的零件表中有一个类别代码,而类别表中不存在?

                    【讨论】:

                      【解决方案11】:

                      这是使用 ANSI 样式联接时某些 Oracle 版本的已知错误。正确的行为是得到 ORA-00918 错误。

                      无论如何,最好指定您的表名;这样,当您碰巧添加一个名称也用于另一个表的新列时,您的查询不会中断。

                      【讨论】:

                        【解决方案12】:

                        这是 Oracle 9i 中的一个错误。如果您使用 ANSI 表示法连接 2 个以上的表,则它不会检测列名中的歧义,并且如果不使用别名,则可能返回错误的列。

                        前面已经提到,在10g中是固定的,所以如果不使用别名,会返回错误。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2016-10-02
                          • 1970-01-01
                          • 2020-09-28
                          • 1970-01-01
                          • 2018-07-31
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多