4

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

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

有人有线索吗?

4

12 回答 12

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: column ambiguously defined

于 2008-09-12T13:06:24.017 回答
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”。

于 2008-09-12T13:04:42.510 回答
2

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

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

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

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

于 2008-09-12T13:29:49.000 回答
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。

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

于 2008-09-15T15:55:35.863 回答
1

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

于 2008-09-12T13:17:42.477 回答
1

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

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

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

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

于 2008-09-12T18:44:39.867 回答
1

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

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

于 2008-09-18T15:55:54.230 回答
0

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

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

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

于 2008-09-12T13:00:56.993 回答
0

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

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

于 2008-09-12T13:19:12.240 回答
0

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

于 2008-09-12T13:42:18.580 回答
0

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

于 2008-09-17T15:57:10.227 回答
0

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

如前所述,它在 10g 中是固定的,因此如果不使用别名,则会返回错误。

于 2011-08-11T18:23:51.523 回答