【问题标题】:ORACLE SQL - Can't get Trigger to obtain Sum/Count of values from that table that is being updatedORACLE SQL - 无法获取触发器以从正在更新的表中获取值的总和/计数
【发布时间】:2015-03-02 08:21:21
【问题描述】:

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

起初我尝试使用 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);

【问题讨论】:

    标签: sql oracle plsql database-trigger


    【解决方案1】:

    我建议在触发器中使用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 及时调用merge 语句驻留在触发器中,或者您可以使用相同的调整方法。

    【讨论】:

    • 谢谢,不知道这个的合并功能。
    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • 谢谢你的解释,我去看看那个网站。
    猜你喜欢
    • 2012-02-08
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 2013-05-25
    • 1970-01-01
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    相关资源
    最近更新 更多