【问题标题】:SQL - Count number of rows in a table before updating itSQL - 在更新表之前计算表中的行数
【发布时间】:2020-09-10 11:06:09
【问题描述】:

我正在做一个关于 Oracle SQL 的大学项目,当我尝试更新表时,我不断收到错误“表 (...) 正在变异,触发器/函数可能看不到它”。我的 participateTeams 表有 3 列 (eventName varchar()、teamId 编号、teamPlace 编号)。

这里的目标是,当我在活动中更新给定团队的 teamPlace 时,该位置必须小于或等于同一活动中参与团队的数量。

我的触发器如下:

Before update on participateTeams
for each row
Declare participants number;
Begin
     select count(*) into participants from participateteams where :New.eventName = participateteams.eventName;

        if(:NEW.teamplace>participants) then
            RAISE_APPLICATION_ERROR(-20250,'O lugar da equipa é inválido');
        end if;
End;
/

根据我的研究,这是因为我正在尝试读取调用触发器的同一张表。 我也尝试将代码的 select 部分导出到函数中,但问题仍然存在。

活动的参与者是团队本身,而不是组成这些团队的个人。示例:如果 A 队、B 队和 C 队参加了一个活动 E,那么同一活动 E 的计数应该是 3。

有什么建议吗?谢谢。

【问题讨论】:

  • 一种方法可能是使用语句级触发器来检查整个表。这不会扩展到非常大的数量,但它可能适用于大学项目。一个改进可能是让行级触发器将更新的团队 ID 存储在包数组变量中,然后让语句级触发器只检查这些。或者,有一个解决方案使用带有约束的提交时刷新物化视图。

标签: sql oracle plsql mutating-table


【解决方案1】:

您想要实现的逻辑正确的触发器是这样的:-

CREATE OR REPLACE TRIGGER TRIG_CHK
AFTER INSERT OR UPDATE OF teamPlace ON participateteams
FOR EACH ROW
DECLARE
participants NUMBER;
BEGIN
 select count(*) into participants from participateteams where :New.eventName = 
 participateteams.eventName;
    if(:NEW.teamplace>participants) then
        RAISE_APPLICATION_ERROR(-20250,'O lugar da equipa é inválido');
    end if;
End;    
update participateteams set teamPlace = 2 where eventName = 'B';

但是当它在尝试更新表参与团队时出现变异表错误时,为了解决这个棘手的情况,我所做的是:-

step1:在包头中声明需要的表列

CREATE OR REPLACE PACKAGE PKG_TEAMS AS
v_eventName participateteams.eventName%type;
v_teamPlace participateteams.teamPlace%type;
end;

step2 : 在行级触发器中初始化变量

CREATE OR REPLACE TRIGGER TRG_row
AFTER INSERT OR UPDATE OF teamPlace
ON participateteams
FOR EACH ROW
BEGIN
PKG_TEAMS.v_eventName := :NEW.eventName;
PKG_TEAMS.v_teamPlace := :NEW.teamPlace;
END;

step3 :Now ,而不是使用 :NEW 伪列,使用全局初始化变量

  CREATE OR REPLACE TRIGGER TRG_stmt
AFTER INSERT OR UPDATE OF teamPlace ON participateteams
DECLARE 
v_participants NUMBER;
BEGIN
SELECT COUNT(*) INTO v_participants FROM participateteams
WHERE eventName = PKG_TEAMS.v_eventName;
 if(PKG_TEAMS.v_teamPlace>v_participants) then
            RAISE_APPLICATION_ERROR(-20250,'CANNOT UPDATE,AGAINST RULES');
        end if;
End; 

【讨论】:

    【解决方案2】:

    变异表错误只是一种痛苦。而你正在尝试做的是相当棘手的。这是一种方法:

    1. 在teams 中添加一列,其中包含参与者的数量。
    2. 使用insert/update 维护此列,并在participateteams 上触发delete。
    3. 编写一个用户定义的函数来获取给定团队的计数。
    4. 使用用户定义的函数在participateteams 中添加check 约束。

    【讨论】:

    • 抱歉,我对问题的描述可能不够清楚,但参与者是团队本身,而不是团队中的个人。例如,在事件 E 中,有 A 队、B 队和 C 队竞争,因此事件 E 的计数应返回 3。
    猜你喜欢
    • 2015-05-09
    • 2019-07-02
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多