5

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

我们在 Oracle DB 版本 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;
/

这是一个示例 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;

注意选择末尾的 ORDER BY 子句。这个 INSERT 语句的结果是这样的:

F1 F2 F3  F4 F5
---------------
E  N  ABC I  I 
E  Y  QWE I  I 
I  Y  GHJ I  I  

如您所见,第 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;

结果是这样的:

F1 F2 F3  F4 F5
---------------
E  N  ABC E  E 
E  Y  QWE O  E 
I  Y  GHJ I  I

再次注意 ORDER BY 子句,它现在对所有行进行排序,而不是第一个插入语句中的前三行。

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

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

PS关于评论:

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

4

2 回答 2

6

这看起来确实像一个错误,但我在错误数据库中找不到明显的匹配项(一些看起来可能,例如 5842445,但模糊或不完全一致)。我只能通过触发器来实现(所以我假设您的插入是针对T1而不是V1_T1转录错误);且仅当F4F5不是CHARVARCHAR2

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;

...和instead of触发器完全如问题所示。

:NEW根据,触发器中的值是错误的DBMS_OUTPUT,但是我认为只有 Oracle 才能弄清楚列数据类型对它的影响。

它也仍然发生在 11.2.0.3 (Linux) 中。有趣的是,如果我将其更改为UNION ALLUNION我会得到稍微不同的结果;在 10g 中,这两列最终为空,在 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

......这甚至更奇怪 - 看起来可能对其他一些错误的修复稍微影响了这个。

所以不是真正的答案;您需要向 Oracle 提出服务请求,我很确定他们只会告诉您删除order by,因为您已经知道它没有任何价值。

对于蒂洛;没有任何计划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 |
----------------------------------------------------------------------------------

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

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

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


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


更进一步的数据类型点......视图必须有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;

但如果我反过来做,它确实会出现问题:

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;
于 2012-09-10T11:45:57.340 回答
0

除非我遗漏了什么,否则整个过程都可以正常工作,并且T1在 Oracle 10g 和 11g 中都按预期将行插入到表中。

create table t1 (f1 varchar2(10),
f2 varchar2(10),
f3 varchar2(10),
f4 varchar2(10),
f5 varchar2(10));

create or replace view v_t1 as select * from t1;

CREATE OR REPLACE TRIGGER V1_T1_BIUD
  INSTEAD OF INSERT OR UPDATE OR DELETE
  ON v_t1
  FOR EACH ROW
DECLARE
  AnyId   NUMBER;
BEGIN
  IF INSERTING THEN
    INSERT INTO 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;

--With UNION ALL
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;

select * from t1;

F1         F2         F3         F4         F5       
---------- ---------- ---------- ---------- ----------
E          N          ABC        E          E          
E          Y          QWE        O          E          
I          Y          GHJ        I          I 

delete from t1;
commit;

--With UNION
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
  SELECT 'E',   'Y',   'QWE',   'O',   'E'   FROM DUAL UNION
  SELECT 'I',   'Y',   'GHJ',   'I',   'I'   FROM DUAL           
) A
ORDER BY 1, 2, 3; 
commit;

select * from t1;

F1         F2         F3         F4         F5       
---------- ---------- ---------- ---------- ----------
E          N          ABC        E          E          
E          Y          QWE        O          E          
I          Y          GHJ        I          I  

果然,当我更改F4F5更改char(10)varchar2(10)(如Alex Poole所述),我可以在 10g 和 11g 中重新创建您的问题。

于 2012-09-10T14:53:43.103 回答