2015-03-02 57 views
1

好吧,我从课程中得到了一些练习练习,我发现了一个我无法弄清楚的问题,即使它听起来很简单。我有这张桌子。我想做一个触发器,在每次更新或向产品表中插入一个值后,它会使用该类别的产品数量和价格总和更新类表。ORACLE SQL - 无法获取触发器以获取正在更新的表中的总和/计数值

起初我尝试使用FOR EACH ROW,但我无法弄清楚如何使它读取产品表新产品值的新条目没有'变异',因为我正在尝试读取正在更新/插入的产品表。即使有一个正确的方法来设置:新的,也无法弄清楚。和:老。以此目的。

尝试过检查产品表并选择变量和总和的游标,但它是同样的问题,整个“变异”问题。

现在我没有了FOR EACH ROW,但它计算和总结所有类的所有产品和价格。我无法弄清楚如何让它为每个班级单独工作。我虽然可能是一个循环和一个计数器,基于最大类增加,但似乎过于复杂。任何帮助,将不胜感激。

drop table class  cascade constraints; 
drop table provider  cascade constraints; 
drop table product  cascade constraints; 


CREATE TABLE class(
class   number(5)  constraint pk_class primary key, 
description  varchar2(20) constraint nn1_class CHECK(description = INITCAP(description) AND description IS NOT NULL), 
tot_product  number(5)  constraint nn2_class CHECK (tot_product >=0 AND tot_product IS NOT NULL), 
tot_price  number(12,2) constraint nn3_class CHECK (tot_price >=0 AND tot_price IS NOT NULL), 
constraint  pk1_class  CHECK (class >=0) 
); 


INSERT INTO class VALUES(1,'Description 1', 0, 0); 
INSERT INTO class VALUES(2,'Description 2', 0, 0); 
INSERT INTO class VALUES(3,'Description 3', 0, 0); 
INSERT INTO class VALUES(4,'Description 4', 0, 0); 
INSERT INTO class VALUES(5,'Description 5', 0, 0); 


CREATE TABLE provider(
provider number(5)  constraint pk_provider primary key, 
description varchar2(20) constraint nn1_provider CHECK(description = INITCAP(description) AND description IS NOT NULL), 
constraint pk1_provider CHECK (provider >=0) 
); 

INSERT INTO provider VALUES(1,'Description 1'); 
INSERT INTO provider VALUES(2,'Description 2'); 
INSERT INTO provider VALUES(3,'Description 3'); 
INSERT INTO provider VALUES(4,'Description 4'); 
INSERT INTO provider VALUES(5,'Description 5'); 


CREATE TABLE product(
product   number(5)  constraint pk_product  primary key, 
description  varchar2(20) constraint nn1_product CHECK (description = INITCAP(description) AND description IS NOT NULL), 
price   number(12,2) constraint nn2_product CHECK (price >=0  AND price IS NOT NULL), 
available  number(5)  constraint nn3_product CHECK (available >=0 AND available IS NOT NULL), 
class   number(5)  constraint fk1_product  references class  NOT NULL,   
provider  number(5)  constraint fk2_product references provider NOT NULL, 
constraint  pk1_product CHECK (product >=0) 
); 




CREATE OR REPLACE TRIGGER tot_class 
AFTER INSERT OR UPDATE ON product 

DECLARE 
e_tot_product number(5); 
e_tot_price  number(12,2); 

BEGIN 

SELECT COUNT(product) INTO e_tot_product 
    FROM product 
    WHERE class = class; 

SELECT SUM(price) INTO e_tot_price 
    FROM product 
    WHERE class = class; 


UPDATE class SET tot_product = e_tot_product, tot_price= e_tot_price WHERE class = class; 
END; 
/


INSERT INTO product VALUES(1,'Description 1', 100, 10, 1, 1); 
INSERT INTO product VALUES(2,'Description 2', 100, 10, 1, 2); 
INSERT INTO product VALUES(3,'Description 3', 100, 10, 2, 1); 
INSERT INTO product VALUES(4,'Description 4', 100, 10, 4, 5); 
INSERT INTO product VALUES(5,'Description 5', 100, 10, 2, 3); 

回答

0

我建议使用MERGE语句在触发类似下面

CREATE OR REPLACE TRIGGER TOT_CLASS 
AFTER INSERT OR UPDATE 
ON PRODUCT 
REFERENCING NEW AS NEW OLD AS OLD 
BEGIN 

MERGE INTO class oldclass 
using 
( 
SELECT a.class,COUNT(b.product) tot_poduct,sum(b.price) tot_price 
    FROM product b 
    join 
    class a 
    on a.class = b.class 
    group by a.class 
) newclass 
on (oldclass.class = newclass.class) 
when matched then 
update set oldclass.tot_product = newclass.tot_poduct, 
oldclass.tot_price = newclass.tot_price; 

END; 
/

这里如果产品类被然后更新,我们必须重新检查旧产品的数量和总价也。因此上述声明将会遇到这样的问题。尽管如此,我仍然不确定在大型数据集上的性能。我建议使用DBMS_JOBS及时调用合并语句驻留在触发器内,或者可以使用相同的调整方法。

+0

谢谢,没有意识到这个合并函数。 – user2429212 2015-03-02 19:45:16

0

所以,你发现了突变。首先,如果你发现你需要处理突变,问问你自己为什么。我会怀疑数据模型或应用程序设计中存在缺陷。无论如何,说了这些,让我们谈一谈突变以及如何避免它。

在触发器代码中有行级触发器并引用构建触发器的表时会发生突变,并且单个SQL语句影响表中的多行。 Oracle不知道如何保持一致性,因为每一行都会重新执行触发器。

处理突变的标准方法是编写一个包,包含三个函数和一个包级数组。这三个函数是initialize(),save_row()和process()。

您需要三个步骤才能使其工作。首先,你需要一个语句级别的BEFORE触发器来调用initialize()函数,以初始化一个包级数组。然后,行级别的BEFORE触发器将调用save_row()函数,该函数将当前行(pk或rowid)保存到数组中,最后是一个statle级别的AFTER触发器,以调用process()读取数组中的行并相应地处理数据。

如果你去http://asktom.oracle.com/并寻找'表突变',你会发现很多回答问题,并附有示例代码。

希望有所帮助。

+0

感谢您的解释,我会检查该网站了。 – user2429212 2015-03-02 19:45:33