4

我们有以下要求:

表1:复合主键version, id

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'Y'
 1        124    'E'   'F'     'Y'

现在对于table1给定的任何插入和更新id,应该使用以下属性(由触发器派生)创建一个新行:

  1. Versionid对于给定的和应该加 1
  2. 大多数当前行应该变为活动状态(active列设置为Y

例如

INSERT INTO table1(id, col1, col2) VALUES (123, 'X', 'Y');

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'N'
 3        123    'X'   'Y'     'Y' 
 1        124    'E'   'F'     'Y'

第 3 行已创建

UPDATE table1 SET col1 = 'F' WHERE id = 124;

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'N'
 3        123    'X'   'Y'     'Y' 
 1        124    'E'   'F'     'N'
 2        124    'F'   'F'     'Y'

最后一行被创建

DELETE FROM dbo.table1 WHERE id = 124;

------------------------------------
version   id     col1  coll2  active
------------------------------------
 1        123    'A'   'B'     'N'
 2        123    'C'   'D'     'N'
 3        123    'X'   'Y'     'Y' 
 1        124    'E'   'F'     'N'
 2        124    'F'   'F'     'N'

id 124 的所有行都变为非活动状态。

这似乎是建模问题,但我们必须使用 TABLE1 和支持触发器来提供此功能。

我们无法根据需要绕过修改表问题select max(version) ,然后插入到同一个表中,任何人都可以提出解决方法吗?

4

2 回答 2

5

我不喜欢数据库设计,太可怕了,但你说你被它困住了。使用带有 INSTEAD OF 触发器的视图怎么样?

1) 将表重命名为例如 TABLE1_BASE

2) 创建视图 TABLE1 为 SELECT * FROM TABLE1_BASE

3) 添加一个 INSTEAD OF 触发器,如下所示:

create trigger table1_io
instead of insert or update or delete on table1
for each row
begin
  if inserting or updating then 
      update table1_base
      set active = 'N'
      where id = :new.id
      and active = 'y';

      insert into table1_base...;
  elsif deleting then
      update table1_base
      set active = 'N'
      where id = :old.id
      and active = 'y';
  end if;
end;

(类似的东西)

于 2012-08-29T12:38:08.927 回答
4

正如您所说,这是一个建模问题,变异表错误通常表明有更好的方法来实现这一点。

有一些方法可以解决这个问题,但它们都不是最佳实践,并且必须权衡增加的复杂性与业务逻辑/表的重要性。如果这是一个键表,您真的想在应该是简单插入的内容上使用复杂的逻辑吗?

方法 1 将更新放在一个包过程中的表上。从调用此过程的触发器运行作业。这意味着您现在有两个事务:原始插入和后续。

方法 2(类似于 1) 创建一个高级队列和对象类型来封装您希望进行的更改。在插入期间添加到队列中。将对象出列并根据需要应用更改。在你的情况下:立即。方法 1 和 2 的优点是初始事务永远不会因为后续清理而停止。

方法 3(可行,但由于复杂性不是最佳实践) 您需要两个触发器:表上的插入后和表上每一行的插入后

两个触发器都调用一个封装你的逻辑的包,它看起来像这样

   CREATE OR REPLACE PACKAGE BODY XYZ AS
   TYPE t_template_rec
   IS
      RECORD (case_id     NUMBER (10), objective_id NUMBER (10));

   TYPE t_template_table IS TABLE OF t_template_rec;

   g_change_table t_template_table
         := t_template_table () ;

   TYPE t_deliv_rec IS RECORD (case_id NUMBER (10), stage_id NUMBER (10));

   TYPE t_deliv_table IS TABLE OF t_deliv_rec;

   g_deliv_table                         t_deliv_table := t_deliv_table ();

表级触发器将主键和数据传递给此过程

   PROCEDURE CHECK_FOR_TEMPLATES (case_id_in     IN NUMBER,
                                  objective_in   IN NUMBER)
   IS

   BEGIN

      g_change_table.EXTEND;
      g_change_table (g_change_table.LAST).case_id := case_id_in;
      g_change_table (g_change_table.LAST).objective_id := objective_in;
   END CHECK_FOR_TEMPLATES;

行级触发器调用同一个包中的不同过程

   PROCEDURE ADD_TEMPLATES_ON_STATEMENT
   IS

      v_count          NUMBER (10);
      v_case_id        NUMBER (10);
      v_objective_id   NUMBER (10);
   BEGIN
      FOR i IN g_change_table.FIRST .. g_change_table.LAST
      LOOP
         SELECT   COUNT ( * )
           INTO   v_count
           FROM   XYZ.DELIVERABLE_TEMPLATE dt
          WHERE   DT.INITIATIVE_OBJECTIVE_ID =
                     g_change_table (i).objective_id;
         --check if there is any work to be done
         IF v_count > 0
         THEN
            v_case_id := g_change_table (i).case_id;
            v_objective_id := g_change_table (i).objective_id;
            --do some work here
         END IF;
      END LOOP;

      g_change_table.delete;
  END ADD_TEMPLATES_ON_STATEMENT;

为了清楚起见,我删除了注释/调试/断言。我正在使用方法#3,一旦我开始工作,它就可以正常工作,但如果我不得不返回并在该表上实现更多业务逻辑,我会小心处理它。

于 2012-08-29T12:36:43.547 回答