1

好的,我从课堂上得到了一些练习,我发现了一个我无法弄清楚的,即使它听起来很简单。我有这张桌子。我想创建一个触发器,在每次更新或向产品表中插入一个值后,它会使用属于该类的产品数量和价格总和来更新类表。

起初我尝试使用 FOR EACH ROW,但我不知道如何制作它以便它读取新条目的类的产品表新产品值而不“变异”,因为我试图读取产品正在更新/插入的表。即使有正确的设置方法也无法弄清楚:new。和:旧的。以此目的。

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

现在我得到它没有 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);
4

2 回答 2

0

所以,你发现了突变。首先,如果你发现你需要处理突变,问问自己为什么。我怀疑数据模型或应用程序设计存在缺陷。然而,话虽如此,让我们谈谈突变以及如何避免它。

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

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

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

如果您访问http://asktom.oracle.com/并搜索“表突变”,您会发现大量已回答的问题,并附有示例代码。

于 2015-03-02T08:59:19.403 回答
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或者您可以使用相同的调整方法。

于 2015-03-02T09:30:04.070 回答