当值是 inserted/updated/deleted to/from 同一 table 中的另一列时,如何让列自动反映值?
How to let a column have values reflected automatically, when value is inserted/updated/deleted to/from another column in the same table?
我创建了 2 个表:INFORMATION
和 FEED
。
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.
现在,我必须 insert/update/delete 值 to/from TOT_REM
,基于 S_AMOUNT
和 TOT_AMOUNT
的 insertion/removal/update。
示例内容为:
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
...
我们需要在 S_AMOUNT
上执行的 update/insert/delete 操作的基础上,借助 TOT_AMOUNT
& [=22] 自动将值插入到 TOT_REM
属性中=].
在任何时候,TOT_REM 都不能小于 0。而且,TOT_REM 需要自动 inserted/removed/updated 这样
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;
问题:
- 这种方法有缺陷吗?我不能通过这种方式实现我想要的吗?[可选]
- 这里有没有引入视图的范围?我无法在那条线上思考!可能是缺乏经验...[可选]
- 有什么更好的方法,可以自动反映 TOT_REM 个值?[必须回答]
我觉得还是创建视图比较好。看看这个
测试数据
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;
在这种情况下,您使用触发器的方法不合适table。我认为很少添加数据,只有在特殊情况下才需要查询。当然,有一些方法可以解决变异 table(包变量、复合触发器、自主事务),但我认为它们只会给你的数据库增加性能问题。
如果这是我的业务问题,我可以从头开始(在你的情况下这可能是不可能的),我会保持 INFORMATION
table 原样,我会删除FEED
中的 TOT_REM
列,我将创建一个看起来像当前 FEED
table 的视图。您可以在视图定义中编写所有必要的逻辑。
添加:
首先,这是一个视图定义;它假设基数 tables INFORMATION
和 FEED
如 OP 所述,FEED
.
中没有 TOT_REM
列
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
;
视图使用外部联接,以包含 INFORMATION
table 中没有任何对应行的 FEED
中的 id
。 (然后,为了处理 TOT_REM
计算中的空值,我使用 nvl()
函数将 NULL
转换为 0
。)
这里是 运行 视图的示例:
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.
现在,实施复杂约束的一种行之有效的方法是使用物化视图。完全检查约束仅在行级别起作用,当条件涉及多个 table 时不能使用。在当前问题中,检查是针对两个 table,而 TOT_REM
取决于 FEED
table 中的其他行 - 因此 FEED
table 无论如何都行不通。
物化视图的做法是定义一个像我创建的视图一样的视图,作为物化视图,用refresh fast on commit
定义它(这样在基础上进行DML操作后立即检查约束tables),并在物化视图上创建检查约束。在手头的问题中,这将是对 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
有一个检查约束以确保它是非负的,并且 FEED
中的 id
是指向 id
中的主键 INFORMATION
,因此对于此版本的视图,外部联接没有透露任何其他信息。 (但是,如果需要,可以包含所有 id
)。
在这里你应该可以用refresh fast on commit
定义视图,并为tot_rem >= 0
的效果添加检查约束。 las,我无法测试它;高级复制(需要创建物化视图日志,refresh fast
又需要它)在我拥有的免费 Oracle Express 版本中不是 available/enabled。尝试对此进行试验 - 它可能是您需要的解决方案。祝你好运!
我创建了 2 个表:INFORMATION
和 FEED
。
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.
现在,我必须 insert/update/delete 值 to/from TOT_REM
,基于 S_AMOUNT
和 TOT_AMOUNT
的 insertion/removal/update。
示例内容为:
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
...
我们需要在 S_AMOUNT
上执行的 update/insert/delete 操作的基础上,借助 TOT_AMOUNT
& [=22] 自动将值插入到 TOT_REM
属性中=].
在任何时候,TOT_REM 都不能小于 0。而且,TOT_REM 需要自动 inserted/removed/updated 这样
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;
问题:
- 这种方法有缺陷吗?我不能通过这种方式实现我想要的吗?[可选]
- 这里有没有引入视图的范围?我无法在那条线上思考!可能是缺乏经验...[可选]
- 有什么更好的方法,可以自动反映 TOT_REM 个值?[必须回答]
我觉得还是创建视图比较好。看看这个
测试数据
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;
在这种情况下,您使用触发器的方法不合适table。我认为很少添加数据,只有在特殊情况下才需要查询。当然,有一些方法可以解决变异 table(包变量、复合触发器、自主事务),但我认为它们只会给你的数据库增加性能问题。
如果这是我的业务问题,我可以从头开始(在你的情况下这可能是不可能的),我会保持 INFORMATION
table 原样,我会删除FEED
中的 TOT_REM
列,我将创建一个看起来像当前 FEED
table 的视图。您可以在视图定义中编写所有必要的逻辑。
添加:
首先,这是一个视图定义;它假设基数 tables INFORMATION
和 FEED
如 OP 所述,FEED
.
TOT_REM
列
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
;
视图使用外部联接,以包含 INFORMATION
table 中没有任何对应行的 FEED
中的 id
。 (然后,为了处理 TOT_REM
计算中的空值,我使用 nvl()
函数将 NULL
转换为 0
。)
这里是 运行 视图的示例:
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.
现在,实施复杂约束的一种行之有效的方法是使用物化视图。完全检查约束仅在行级别起作用,当条件涉及多个 table 时不能使用。在当前问题中,检查是针对两个 table,而 TOT_REM
取决于 FEED
table 中的其他行 - 因此 FEED
table 无论如何都行不通。
物化视图的做法是定义一个像我创建的视图一样的视图,作为物化视图,用refresh fast on commit
定义它(这样在基础上进行DML操作后立即检查约束tables),并在物化视图上创建检查约束。在手头的问题中,这将是对 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
有一个检查约束以确保它是非负的,并且 FEED
中的 id
是指向 id
中的主键 INFORMATION
,因此对于此版本的视图,外部联接没有透露任何其他信息。 (但是,如果需要,可以包含所有 id
)。
在这里你应该可以用refresh fast on commit
定义视图,并为tot_rem >= 0
的效果添加检查约束。 las,我无法测试它;高级复制(需要创建物化视图日志,refresh fast
又需要它)在我拥有的免费 Oracle Express 版本中不是 available/enabled。尝试对此进行试验 - 它可能是您需要的解决方案。祝你好运!