左联接中的歧义(仅适用于Oracle?)
我的老板在我创建的查询中发现了一个错误,尽管查询结果证明他是正确的,但我不理解该错误的原因。这是修复之前的查询(简化版):
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 Server中。
从Oracle文档中可以看出,似乎只有在选择列表中两次选择列名,或者在选择列表中一次选择列名,然后再在其他地方(如order by子句)选择列名时,才会抛出该异常。
也许我们发现了"未记录的功能" :)
回答
有趣的是,SQL Server会抛出一个错误(应该如此)
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"不明确。
回答
Here's the query (simplified version)
我认为通过简化查询,我们可以删除导致错误的真正原因:-)
我们正在使用哪个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:列的定义不明确
回答
我正在使用Oracle 9.2.0.8.0。并且确实给出了错误" ORA-00918:列定义不明确"。
回答
像HollyStyles一样,我在Oracle文档中找不到任何可以解释我们所看到的内容的东西。
PostgreSQL,DB2,MySQL和MSSQL都因为模棱两可而拒绝运行第一个查询。
回答
根据我的经验,如果像这样创建查询,那么当存在这样的字段重叠时,数据结果将从连接的右侧而不是左侧拉CATCD。
因此,由于此联接将具有来自PARTS的所有记录,而仅具有来自CATEGORIES的一些记录,因此,只要右侧没有数据,则CATCD字段中的值为NULL。
通过从PARTS(即左侧)显式定义列,我们将获得一个非null值,前提是该字段在PARTS中具有数据。
请记住,使用LEFT JOIN只能保证左侧表中字段中的数据,右侧很可能有空列。
回答
@帕特:对于查询,我在这里遇到同样的错误。我的查询比我最初发布的查询稍微复杂一点。我现在正在研究一个可重现的简单示例。
回答
恐怕我不能告诉你为什么你没有例外,但是我可以推测为什么它选择了CATEGORIES版本的列而不是PARTS版本的列。
As far as I understood, in the case of left joins, the "main" table in the query (PARTS) has precedence in ambiguity
目前尚不清楚,从概念上看,查询中的"主"是指左联接中的左表,还是"驱动"表。我们编写的查询在实际执行中不一定是"主"表。
我的猜测是,Oracle在执行查询时只是使用它命中的第一个表中的列。而且,由于SQL中的大多数单独操作不需要先击中一个表,因此DBMS将在解析时决定哪个是最有效的扫描对象。尝试获取查询的执行计划。我怀疑它可能揭示它先击中类别,然后击中PARTS。
回答
这可能是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。
但是我不知道这是原因还是结果。
回答
我们应该问自己一个更大的问题是:为什么零件表中没有类别表中的类别代码?
回答
当使用ANSI样式的联接时,这是某些Oracle版本的已知错误。正确的行为是得到ORA-00918错误。
无论如何,始终最好指定表名。这样,当我们碰巧添加一个名称也用于另一个表的新列时,查询不会中断。