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

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

有人有线索吗?

有帮助吗?

解决方案

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

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

目前尚不清楚“主”是否只是指左连接中的左表,或者“驱动”表,正如您从概念上看到的查询一样......但无论哪种情况,您所编写的查询中所看到的“主”表不一定是该查询实际执行中的“主”表。

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

其他提示

这是查询(简化版)

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

你用的是什么版本的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:各列定义不明

对抛出错误的 SQL Server 很感兴趣(因为它应该)

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

服务器:MSG 209,级别16,状态1,第1行含糊不清的列名称“ ID”。

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

服务器:MSG 209,级别16,状态1,第1行含糊不清的列名称“ ID”。

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

因此,由于此连接将包含 PARTS 中的所有记录,仅从 CATEGORIES 中提取一些记录,因此只要右侧没有数据,CATCD 字段中就会出现 NULL。

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

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

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

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

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

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

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

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

据我所知 甲骨文文档, ,似乎只有当您在选择列表中选择列名称两次,或者在选择列表中选择一次,然后在其他地方再次选择(例如 order by 子句)时,它才会抛出。

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

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

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

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

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

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

正如已经提到的,它在10g中被修复,因此如果不使用别名,将会返回错误。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top