Oracle 10:INSERT进入视图时难以理解的行为?

bl4*_*0l7 5 oracle triggers view oracle10g

我们这里有一个奇怪的问题,我们无法向自己解释.

我们在Oracle DB Version 10.2.0.5.8中有一个视图.该视图使用INSTEAD OF触发器.

这是触发器的代码:

CREATE OR REPLACE TRIGGER V1_T1_BIUD
  INSTEAD OF INSERT OR UPDATE OR DELETE
  ON V1_T1
  FOR EACH ROW
DECLARE
  AnyId   NUMBER;
BEGIN
  IF INSERTING THEN
    INSERT INTO Table T1 (
       F1, F2, F3, F4, F5
    ) VALUES (
       :new.F1, :new.F2, :new.F3, :new.F4, :new.F5
    );
  ELSIF UPDATING THEN
    UPDATE T1 SET F1 = :new.F1,
                  F2 = :new.F2,
                  F3 = :new.F3,
                  F4 = :new.F4,
                  F5 = :new.F5
    WHERE F1 = :old.F1;
  ELSIF DELETING THEN
    DELETE FROM T1
    WHERE F1 = :old.F1;
  END IF;
END;
/
Run Code Online (Sandbox Code Playgroud)

这是一个INSERT语句示例:

INSERT INTO V_T1 (
  F1, F2, F3, F4, F5
)
SELECT A.V, A.S, A.F, A.T, A.Z
FROM (
  SELECT 'E' V, 'N' S, 'ABC' F, 'E' T, 'E' Z FROM DUAL UNION ALL 
  SELECT 'E',   'Y',   'QWE',   'O',   'E'   FROM DUAL UNION ALL
  SELECT 'I',   'Y',   'GHJ',   'I',   'I'   FROM DUAL           
) A
ORDER BY 1, 2, 3;
COMMIT;
Run Code Online (Sandbox Code Playgroud)

注意select的末尾的ORDER BY子句.这个INSERT语句的结果是这样的:

F1 F2 F3  F4 F5
---------------
E  N  ABC I  I 
E  Y  QWE I  I 
I  Y  GHJ I  I  
Run Code Online (Sandbox Code Playgroud)

如您所见,第4列和第5列未正确填充所有其他数据行中最后一个数据行的值.

如果我们像这样更改INSERT语句:

INSERT INTO V_T1 (
  F1, F2, F3, F4, F5
)
SELECT A.V, A.S, A.F, A.T, A.Z
FROM (
  SELECT 'E' V, 'N' S, 'ABC' F, 'E' T, 'E' Z FROM DUAL UNION ALL 
  SELECT 'E',   'Y',   'QWE',   'O',   'E'   FROM DUAL UNION ALL
  SELECT 'I',   'Y',   'GHJ',   'I',   'I'   FROM DUAL           
) A
ORDER BY 1, 2, 3, 4, 5;
COMMIT;
Run Code Online (Sandbox Code Playgroud)

结果是这样的:

F1 F2 F3  F4 F5
---------------
E  N  ABC E  E 
E  Y  QWE O  E 
I  Y  GHJ I  I
Run Code Online (Sandbox Code Playgroud)

再次,请注意ORDER BY子句,它现在命令所有行而不是第一个insert语句中的前三行.

编辑:如果省略ORDER BY子句,结果也符合预期(例如,如示例2所示).

有人可以向我解释这种行为吗?

PS关于评论:

我今天没有时间调查或提供有关此主题的更多信息.我将在我们的数据库上创建一个完整的示例,并在接下来的几天内将其发布到此处.感谢您的耐心等待!

Ale*_*ole 6

这看起来像一个错误,但我找不到bug数据库中的明显匹配(一些看起来可能,如5842445,但是模糊或不完全排队).我只能通过触发器实现它(所以我假设你的插入是反对T1而不是V1_T1转录错误); 且仅当F4F5CHAR不是VARCHAR2:

create table t1 (f1 varchar2(2), f2 varchar2(2), f3 varchar2(3),
    f4 char(2), f5 char(2));

create view v1_t1 as select * from t1;
Run Code Online (Sandbox Code Playgroud)

...和instead of问题中显示的触发器完全相同.

根据:NEW,触发器内部的值是错误的,DBMS_OUTPUT但是受列数据类型影响的方式只有Oracle能够理解我认为.

它仍然发生在11.2.0.3(Linux)中.有趣的是,如果我改变UNION ALL到只是UNION我得到略有不同的结果; 在10g中,两列最终为null,在11g中它们具有x:

insert into v1_t1 (
  F1, F2, F3, F4, F5
)
SELECT A.V, A.S, A.F, A.T, A.Z
FROM (
    SELECT 'E' V, 'N' S, 'ABC' F, 'E' T, 'E' Z FROM DUAL UNION
    SELECT 'E',   'Y',   'QWE',   'O',   'E'   FROM DUAL UNION
    SELECT 'I',   'Y',   'GHJ',   'I',   'I'   FROM DUAL
) A
ORDER BY 1, 2, 3;

3 rows created.

select * from v1_t1;

F1 F2 F3  F4 F5
-- -- --- -- --
E  N  ABC x  x
E  Y  QWE x  x
I  Y  GHJ x  x
Run Code Online (Sandbox Code Playgroud)

...这甚至更奇怪 - 看起来可能修复了其他一些bug对这个有点影响.

所以不是一个真正的答案; 你需要向Oracle发出一个服务请求,我很确定他们只是告诉你删除order by它,因为它没有任何价值,正如你已经知道的那样.

对于Thilo; 没有任何计划order by(11g):

----------------------------------------------------------------------------------
| Id  | Operation                | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |       |     3 |    51 |     9  (34)| 00:00:01 |
|   1 |  LOAD TABLE CONVENTIONAL | V1_T1 |       |       |            |          |
|   2 |   VIEW                   |       |     3 |    51 |     9  (34)| 00:00:01 |
|   3 |    SORT UNIQUE           |       |     3 |       |     9  (78)| 00:00:01 |
|   4 |     UNION-ALL            |       |       |       |            |          |
|   5 |      FAST DUAL           |       |     1 |       |     2   (0)| 00:00:01 |
|   6 |      FAST DUAL           |       |     1 |       |     2   (0)| 00:00:01 |
|   7 |      FAST DUAL           |       |     1 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

并计划order by 1,2,3 1,2,3,4,5 - 相同的计划哈希值(11克):

----------------------------------------------------------------------------------
| Id  | Operation                | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |       |     3 |    51 |    10  (40)| 00:00:01 |
|   1 |  LOAD TABLE CONVENTIONAL | V1_T1 |       |       |            |          |
|   2 |   SORT ORDER BY          |       |     3 |    51 |    10  (40)| 00:00:01 |
|   3 |    VIEW                  |       |     3 |    51 |     9  (34)| 00:00:01 |
|   4 |     SORT UNIQUE          |       |     3 |       |     9  (78)| 00:00:01 |
|   5 |      UNION-ALL           |       |       |       |            |          |
|   6 |       FAST DUAL          |       |     1 |       |     2   (0)| 00:00:01 |
|   7 |       FAST DUAL          |       |     1 |       |     2   (0)| 00:00:01 |
|   8 |       FAST DUAL          |       |     1 |       |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

我看到从其他表中选择相同类型的损坏,但前提是子查询中的结果在被排序之前已经联合; 虽然然后我得到空值而不是x.(我简单地想知道它是否x来自dual它自己,但是dummy大写X,这显示小写x).


在@ Annjawn的评论之后,将插入更改V1_T1为直接插入T1工作正常(即插入了正确的值),并且奇怪地具有相同的计划哈希,即使它显示表名而不是Name列中的视图.与任何工作UNION或者UNION ALL,也和两个10gR2中和11gR2的.我猜,这似乎是工会混淆的触发器.


进一步到数据类型点...视图必须有char列,表不一定,这不是一个惊喜,因为视图上的触发器似乎是问题.如果我使用char列设置表但varchar2在视图中将它们转换为表格,那么我没有看到问题:

create table t1 (f1 varchar2(2), f2 varchar2(2), f3 varchar2(3),
    f4 char(2), f5 char(2));

create view v1_t1 as select f1, f2, f3, cast(f4 as varchar(2)) f4,
    cast(f5 as varchar(2)) f5
from t1;
Run Code Online (Sandbox Code Playgroud)

但如果我这样做,反过来确实表现出问题:

create table t1 (f1 varchar2(2), f2 varchar2(2), f3 varchar2(3),
    f4 varchar(2), f5 varchar(2));

create view v1_t1 as select f1, f2, f3, cast(f4 as char(2)) f4,
    cast(f5 as char(2)) f5
from t1;
Run Code Online (Sandbox Code Playgroud)