0

我创建了 2 个表:INFORMATIONFEED

INFORMATION has 2 attributes : ID(Primary Key), TOT_AMOUNT.

FEED has 4 attributes : ID(Foreign key refer INFORMATION(ID)), S_AMOUNT, S_DATE, TOT_REM.

现在,我必须TOT_REM根据 和 的插入/删除/更新S_AMOUNT向/从 插入/更新/删除值TOT_AMOUNT

样本内容为:

INFORMATION Table
------------------
ID   |  TOT_AMOUNT
1    |    100
2    |    20
3    |    50
...

               FEED Table
----------------------------------------
ID   |   S_AMOUNT   |  S_DATE  | TOT_REM 
1    |     10       |10.10.2010|   90
1    |     10       |13.10.2010|   80
1    |     30       |17.10.2013|   50
1    |     10       |20.10.2016|   40
...

我们需要在&的帮助下TOT_REM,根据对 执行的更新/插入/删除操作自动将值插入到属性中。S_AMOUNTTOT_AMOUNTS_AMOUNT

在任何时候,TOT_REM 都不能小于 0。并且,TOT_REM 需要自动插入/删除/更新,这样

TOT_REM for i(at a specific date) = (TOT_AMOUNT for ID=i) - 
                                    SUM(S_AMOUNT of all instances of ID=i,
                                    which is later than the S_DATE for ID=i); 

所以,假设我们删除第二个元组(1,10,'13.10.2010',80),反映的状态BR_FEED应该是:

               FEED Table
----------------------------------------
ID   |   S_AMOUNT   |  S_DATE  | TOT_REM 
1    |     10       |10.10.2010|   90
1    |     30       |17.10.2013|   60
1    |     10       |20.10.2016|   50
...

我写了一个触发器,显示失败

ORA-04091: table SSUMAN.FEED is mutating, trigger/function may not see it

触发器的代码是:

CREATE OR REPLACE TRIGGER BR_INSERT_TRB
AFTER DELETE OR INSERT OR UPDATE OF S_AMOUNT ON FEED
FOR EACH ROW
BEGIN

IF DELETING THEN 
UPDATE  FEED bf
SET     bf.TOT_REM = bf.S_AMOUNT + :OLD.S_AMOUNT;
END IF;

IF INSERTING THEN 
INSERT INTO FEED (TOT_REM) VALUES(
((SELECT TOT_AMOUNT FROM INFORMATION bi WHERE bi.ID=:NEW.ID) - 
(SELECT SUM(S_AMOUNT) FROM FEED bf where bf.ID=:NEW.ID) - 
:NEW.S_AMOUNT);
END IF;

IF UPDATING THEN 
UPDATE  FEED bf
SET     bf.TOT_REM = (SELECT TOT_AMOUNT FROM BR_INFORMATION bi WHERE bi.ID=bf.ID) - 
(SELECT SUM(S_AMOUNT) FROM FEED bf where bf.ID=:NEW.ID) - 
 :NEW.S_AMOUNT
WHERE   :NEW.ID IS NOT NULL;
END IF;   

END;

问题 :

  1. 这种方法有缺陷吗?我不能通过这种方式实现我想要的吗?[可选]
  2. 有没有可以在这里看到的范围?我无法在那条线上思考!可能,缺乏经验... [可选]
  3. 有没有更好的方法,以便自动反映 TOT_REM 值?[必须回答]
4

2 回答 2

1

如果这是我的业务问题,并且我可以从头开始(在您的情况下这可能是不可能的),我会保持INFORMATION表原样,我会TOT_REM从 中删除列FEED,我会创建一个看起来像当前FEED表。您可以在视图定义中编写所有必要的逻辑。

添加

首先,这是一个视图定义;它假定基表INFORMATIONFEED如 OP 所述,TOT_REMFEED.

create view remaining_balance (id, s_amount, s_date, tot_rem) as
select i.id, f.s_amount, f.s_date, 
       i.tot_amount - nvl(sum(f.s_amount) over (partition by f.id order by f.s_date), 0)
from   information i left outer join feed f
                     on i.id = f.id
;

该视图使用外连接,以包括idINFORMATION中没有任何对应行的FEED. (然后,为了处理 的计算中的空值TOT_REM,我使用该nvl()函数转换NULL0。)

这是运行视图的示例:

 SQL> select * from information;

        ID TOT_AMOUNT
---------- ----------
         1        100
         2         20
         3         50

3 rows selected.

SQL> select * from feed;

        ID   S_AMOUNT S_DATE
---------- ---------- ----------
         1         10 2010-10-10
         1         10 2010-10-13
         1         30 2010-10-17
         1         10 2016-10-20

4 rows selected.

SQL> select * from remaining_balance order by id, s_date;

        ID   S_AMOUNT S_DATE        TOT_REM
---------- ---------- ---------- ----------
         1         10 2010-10-10         90
         1         10 2010-10-13         80
         1         30 2010-10-17         50
         1         10 2016-10-20         40
         2                               20
         3                               50

6 rows selected.

现在,一种行之有效的实施复杂约束的方法是使用物化视图。彻底检查约束仅适用于行级别,当条件涉及多个表时不能使用。在当前问题中,检查是针对两个表,并且TOT_REM取决于表中的其他行FEED- 因此表中的约束FEED无论如何都不起作用。

物化视图方法是定义一个像我创建的视图一样的视图,作为物化视图,用它来定义refresh fast on commit(以便在对基表进行 DML 操作后立即检查约束),并在物化视图上创建检查约束看法。在手头的问题中,这将是一个检查TOT_RM >= 0

唉,refresh fast on commit当视图定义使用分析函数时,被禁止(至少在最近的 Oracle 版本 11.2,这是我所拥有的)。我使用了sum()函数的解析版本,所以这行不通。

然而,定义不同的物化视图似乎是有道理的,如下所示:

create materialized view remaining_balance (id, tot_rem) as
select i.id, i.tot_amount - f.sum_s_amount
from   information i inner join (select   id, sum(s_amount) as sum_s_amount 
                                 from     feed 
                                 group by id) f
                     on i.id = f.id
;

SQL> select * from remaining_balance;

        ID    TOT_REM
---------- ----------
         1         40

我不再使用外部连接,因为它只应该显示剩余余额。我假设INFORMATION有一个检查约束,TOT_AMOUNT以确保它是非负的,并且idinFEED是指向 in 的主键idINFORMATION所以对于这个版本的视图,外部连接没有透露额外的信息。(但是,如果需要,它可以包括所有id的)。

在这里,您应该能够定义视图,refresh fast on commit并添加一个检查约束到tot_rem >= 0. 唉,我无法测试它;在我拥有的免费 Express 版本的 Oracle 中,高级复制(需要创建物化视图日志,而这反过来又需要refresh fast)不可用/启用。尝试对此进行试验 - 它可能是您需要的解决方案。祝你好运!

于 2016-07-21T14:24:54.657 回答
1

我认为创建视图更好。看这个

测试数据

create table feed(ID,S_AMOUNT,S_DATE) as  (
  SELECT 1,10, TO_DATE('10.10.2010','dd.mm.yyyy') FROM dual UNION all
  SELECT 1,10,TO_DATE('13.10.2010','dd.mm.yyyy') FROM dual UNION all
  SELECT 1,30,TO_DATE('17.10.2013','dd.mm.yyyy') FROM dual UNION all
  SELECT 2,10,TO_DATE('20.10.2016','dd.mm.yyyy') FROM dual)

create table INFORMATION (id, TOT_AMOUNT) as (
  SELECT 1,100 FROM DUAL UNION ALL
  SELECT 2,20 FROM DUAL UNION ALL
  SELECT 3,50 FROM DUAL)

询问

create or replace view result_feed as
  SELECT f.*,i.TOT_AMOUNT - NVL(SUM(S_AMOUNT) OVER(PARTITION BY f.ID ORDER BY f.S_DATE),0) AS tot_rem FROM FEED f, INFORMATION i
    WHERE f.ID = i.id
  ORDER BY f.ID, f.S_DATE;
  -- used NVL to prevent side-effect of null values


  SELECT * from RESULT_FEED;

您使用 with trigger 的方法不适合这种情况。我认为很少添加数据,仅在特殊情况下才需要查询。当然,有一些方法可以解决变异表(包变量、复合触发器、自主事务),但我认为它们只会给您的数据库添加性能问题。

于 2016-07-21T13:31:28.293 回答