Oracle漏洞? SELECT不返回dupes,SELECT的INSERT具有重复的行

时间:2009-12-08 17:10:05

标签: sql oracle plsql oracle11g

我正在从我正在处理的Oracle实例中获得一些奇怪的行为。这是Itanium上的11gR1,没有RAC,没什么特别的。最终,我在数据仓库场景中将数据从一个Oracle实例移动到另一个实例。

我有一个在数据库链接上运行的半复杂视图; 4个内部连接在大型桌子上,5个左连接在中型桌子上。

这就是问题所在:当我在SQL Developer(或SQL * Plus)中测试视图时,它看起来很好,没有任何重复。但是,当我实际使用视图将数据插入表中时,我得到了大量的欺骗。

编辑: - 数据进入空表。查询中的所有表都在数据库链接上。传递给查询的唯一事情是日期(例如INSERT INTO目标SELECT * FROM视图WHERE view.datecol = dQueryDate) -

我尝试将一个ROW_NUMBER()函数添加到select语句中,由PK为视图分区。所有行都返回编号为1.但是,同样的语句作为插入运行生成与之前相同的dupe,现在方便编号。每个键的欺骗行数不一样。有些记录存在4次,有些只存在一次。

我发现这种行为非常令人困惑。 :)它让我想起了使用Teradata,你有SET表(仅限唯一行)和MULTISET表(允许重复行),但Oracle没有这样的功能。

将行返回到客户端的select应该与将这些行插入另一个位置的select相同。我无法想象这种情况发生的合理原因,但也许我正在遭受想象力的失败。 ;)

我想知道是否有其他人经历过这个或者这个平台上是否有错误。

感谢@Gary,我可以通过使用“我的查询}的EXPLAIN PLAN来了解这个问题;”和“SELECT * FROM TABLE(dbms_xplan.display);”。对于INSERT,实际使用的解释与SELECT非常不同。

对于SELECT,大多数计划操作是“TABLE ACCESS BY INDEX ROWID”和“INDEX UNIQUE SCAN”。 “谓词信息”块包含查询中的所有连接和过滤器。最后它说“注意 - 完全远程声明”

对于INSERT,没有对索引的引用。 'Predicate Information'块只有三行,一个新的'Remote SQL'块显示 9 小型SQL语句。

数据库已将我的查询拆分为9个子查询,然后尝试在本地加入它们。通过运行较小的选择,我找到了重复的来源。

我认为这是围绕远程链接的Oracle编译器中的错误。它在重写SQL时会产生逻辑缺陷。基本上编译器没有正确应用WHERE子句。我只是测试它并给它一个5个键的IN列表来带回来。 SELECT带回5行。 INSERT将77,000多行放入目标,完全忽略IN列表。

{仍在寻找强制正确行为的方法,我可能不得不要求在远程数据库上创建视图,尽管从开发角度来看这并不理想。当我让它工作时我会编辑它...}

9 个答案:

答案 0 :(得分:6)

似乎是Oracle Bug,我们发现以下内容如下: 如果您希望“insert into select ...”的工作方式与“select ...”相同,则可以将您的选择打包到子选择中。

例如:

select x,y,z from table1, table2, where ...

- >没有重复

insert into example_table
select x,y,z from table1, table2, where ...

- >重复错误

insert into example_table
select * from (
       select x,y,z from table1, table2, where ...
)

- >没有重复

此致

答案 1 :(得分:3)

我想到的一件事是,通常SELECT的优化器计划会更喜欢FIRST_ROWS计划尽早将行返回给调用者,但INSERT ... SELECT将更喜欢ALL_ROWS计划,因为它将具有提供完整的数据集。 我使用DBMS_XPLAN.DISPLAY_CURSOR(使用V $ SQL中的sql_id)检查查询计划。

  

我正在运行一个半复杂的视图   通过数据库链接; 4个内部连接   大桌子和5个左连接   中型桌子。   ...   查询中的所有表都已打开   数据库链接

再次,一个潜在的问题点。如果SELECT中的所有表都在数据库链接的另一端,则整个查询将被发送到远程数据库并返回结果集。一旦抛出INSERT,本地数据库更有可能负责查询并从子表中提取所有数据。但这可能取决于视图是在本地数据库还是远程数据库中定义的。在后一种情况下,就本地优化器而言,只有一个远程对象,它从中获取数据,远程数据库将进行连接。

如果你只是去远程数据库并在那里的表上进行INSERT会发生什么?

答案 2 :(得分:1)

这是Oracle处理数据库链接加入时的错误。我有一个更简单的情况,不涉及INSERT与SELECT。如果我远程运行我的查询,我会得到重复的行,但如果我在本地运行它,我不会。查询之间的唯一区别是附加到远程查询中的表的“@ ...”。我使用Oracle SQL Developer 3.0从10.2数据库查询9i数据库。

这比Oracle中的错误更加愚蠢,这会阻止您加入总列数超过1000的表,这在查询ERP系统时非常容易。不,错误消息与列数太多的表无关。

它几乎和其他Oracle数据库错误一样愚蠢,它禁止使用ANSI语法查询包含LOB定位符的表。只有Oracle语法有效!

答案 3 :(得分:0)

我有几种选择。

  1. 您看到的傻瓜已经在目的地表中了?

  2. 如果在您的选择中,您引用了您正在插入的表格(?),那么插入内容与您合并中的选择进行交互

    插入...选择......从......

  3. 以这种方式(笛卡尔产品?)创建重复项

答案 4 :(得分:0)

我不禁想到,也许你正在经历与桌子相关的其他东西的副作用。是否存在可能操纵数据的触发器?

答案 5 :(得分:0)

你是如何确定原始表中没有欺骗的?

正如其他人所说,这似乎是对这种奇怪行为最简单的解释。

答案 6 :(得分:0)

仔细检查JOIN。您可能在各个表中没有重复项,但是未指定的连接可能会导致意外CROSS JOIN s,因此您的结果集由于多重性而具有重复项,并且在插入时会违反目标表中的唯一性约束。

在这种情况下我做的是将查询嵌套在视图或CTE中,并尝试直接从SELECT检测重复项:

WITH resultset AS (
    -- blah, blah
)
SELECT a, b, c, COUNT(*)
FROM resultset
GROUP BY a, b, c
HAVING COUNT(*) > 1

答案 7 :(得分:0)

我建议您对正在运行的查询进行计划,并在那里寻找CARTESIAN JOIN。这可能表示导致重复行的缺失条件。

答案 8 :(得分:0)

AS @Pop已经建议如果您在插入运行时在SQLPlus中使用不同的登录到登录,则可能会发生此行为。 (即如果其他登录名具有同名的表/视图/同义词)