左连接中的歧义(仅限oracle?)

时间:2008-09-12 12:37:35

标签: sql oracle join

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

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) ;

有人有线索吗?

12 个答案:

答案 0 :(得分:6)

  

这是查询(简化版)

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

您使用的是哪个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:列模糊定义

答案 1 :(得分: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'。

答案 2 :(得分:2)

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

因为这个连接将包含来自PARTS的所有记录,只有一些来自CATEGORIES的记录,只要右侧没有数据,你就会在CATCD字段中得到NULL。

通过从PARTS(即左侧)明确定义列,您将获得非空值,假设该字段在PARTS中具有数据。

请记住,对于LEFT JOIN,您只是左表中字段中的保证数据,右侧可能是空列。

答案 3 :(得分:2)

这可能是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。

但我不知道这是一个原因还是影响。

答案 4 :(得分:1)

我正在使用Oracle 9.2.0.8.0。它确实给出错误“ORA-00918:列模糊定义”。

答案 5 :(得分:1)

我担心我不能告诉你为什么你没有得到例外,但我可以假设为什么它选择了CATTSORIES版本的专栏而不是PARTS版本。

  

据我所知,在左连接的情况下,查询(PARTS)中的“主”表优先于歧义

目前尚不清楚“main”是指简单地表示左连接中的左表,还是“驱动”表,正如您在概念上看到的那样...但在任何一种情况下,您所看到的都是“主” “在您编写查询时,表格不一定是该查询实际执行中的”主“表。

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

答案 6 :(得分:1)

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

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

答案 7 :(得分:0)

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

Oracle docs中可以看出,如果你在选择列表中选择两次列名,或者在选择列表中选择一次,然后在其他地方再次使用order by子句,它就会抛出。

也许你已经发现了一个'未记录的功能':)

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

@Pat:我的查询中出现同样的错误。我的查询比我最初发布的内容稍微复杂一点。我现在正在研究一个可重现的简单例子。

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

如前所述,它固定为10g,因此如果不使用别名,将返回错误。