ORACLE PL/SQL:我的SELECT COUNT语句不适用于触发器
因此,我想创建一个触发器,如果表中的记录数超过16,它将触发一条错误消息。我尝试使用下面的代码,但没有发生任何事情,我仍然能够在16之后插入更多的记录ORACLE PL/SQL:我的SELECT COUNT语句不适用于触发器,sql,database,oracle,plsql,triggers,Sql,Database,Oracle,Plsql,Triggers,因此,我想创建一个触发器,如果表中的记录数超过16,它将触发一条错误消息。我尝试使用下面的代码,但没有发生任何事情,我仍然能够在16之后插入更多的记录 create or replace trigger trg_ins_jim before insert on JIM for each row declare v_id_end RESULTS.id%type; v_prices RESULTS.PRICE%type; v_count NUMBER(20); begi
create or replace trigger trg_ins_jim before insert on JIM for each row
declare
v_id_end RESULTS.id%type;
v_prices RESULTS.PRICE%type;
v_count NUMBER(20);
begin
v_prices := :new.age + :new.price;
SELECT COUNT(*) INTO v_count FROM JIM;
IF INSERTING AND v_count<=16 THEN
insert into RESULTS(ID,PRICE) values(:NEW.ID,v_prices);
ELSIF v_count>16 THEN
Raise_Application_Error (-20343, 'Too many records');
END IF;
end;
我认为select语句的设置方式有问题,因为当我将v_count更改为:new.ID只是为了检查是否仍然存在相同的问题时,如果ID号大于16,我会收到一条错误消息,这是一个BEFORE触发器,因此新行不包括在select count*中,因为它还不在表中
尝试检查是否小于16,而不是小于或等于16。更改此项:
IF INSERTING AND v_count<=16 THEN
。。。为此:
IF INSERTING AND v_count<16 THEN
这不是触发器适用的问题。让我们观察不同类型的DML触发器会发生什么
create table jim (id number, age number, price number);
create table results (id number, price number);
--------------------------------------------------------------
create or replace trigger trg_ins_jim_bir --Before insert row
before insert on jim
for each row
declare
v_id_end results.id%type;
v_prices results.price%type;
v_count number(20);
begin
v_prices := :new.age + :new.price;
select count(*) into v_count from jim;
if v_count<=16 then
insert into results(id,price) values(:new.id,v_prices);
else
raise_application_error (-20343, 'Too many records');
end if;
end;
/
alter trigger trg_ins_jim_bir disable;
/
--------------------------------------------------------------
create or replace trigger trg_ins_jim_birat --Before insert row autonomous transaction
before insert on jim
for each row
declare
PRAGMA AUTONOMOUS_TRANSACTION;
v_prices results.price%type;
v_count number(20);
begin
v_prices := :new.age + :new.price;
select count(*) into v_count from jim;
if v_count<=16 then
insert into results(id,price) values(:new.id,v_prices);
commit;
else
raise_application_error (-20344, 'Too many records');
end if;
end;
/
alter trigger trg_ins_jim_birat disable;
/
--------------------------------------------------------------
create or replace trigger trg_ins_jim_air --After insert row
after insert on jim
for each row
declare
v_id_end results.id%type;
v_prices results.price%type;
v_count number(20);
begin
v_prices := :new.age + :new.price;
select count(*) into v_count from jim;
if v_count<=16 then
insert into results(id,price) values(:new.id,v_prices);
else
raise_application_error (-20345, 'Too many records');
end if;
end;
/
alter trigger trg_ins_jim_air disable;
/
--------------------------------------------------------------
create or replace trigger trg_ins_jim_airat --After insert row autonomous transaction
after insert on jim
for each row
declare
PRAGMA AUTONOMOUS_TRANSACTION;
v_prices results.price%type;
v_count number(20);
begin
v_prices := :new.age + :new.price;
select count(*) into v_count from jim;
if v_count<=16 then
insert into results(id,price) values(:new.id,v_prices);
commit;
else
raise_application_error (-20346, 'Too many records');
end if;
end;
/
alter trigger trg_ins_jim_airat disable;
/
--------------------------------------------------------------
create or replace trigger trg_ins_jim_ais -- After insert statement
after insert on jim
declare
v_count number(20);
begin
select count(*) into v_count from jim;
if v_count>16 then
raise_application_error (-20347, 'Too many records');
end if;
end;
/
alter trigger trg_ins_jim_ais disable;
/
--------------------------------------------------------------
-- Following run in sqldeveloper
alter trigger trg_ins_jim_bir enable;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
/*
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15
Error report -
ORA-04091: table BOB.JIM is mutating, trigger/function may not see it
ORA-06512: at "BOB.TRG_INS_JIM_BIR", line 7
ORA-04088: error during execution of trigger 'BOB.TRG_INS_JIM_BIR'
This is because a row level trigger CONNOT refer to the table that caused it to fire.
*/
select 'Jim', count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 0
Results 0
OK Nothing inserted.
*/
alter trigger trg_ins_jim_bir disable;
alter trigger trg_ins_jim_air enable;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
/*
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15
Error report -
ORA-04091: table BOB.JIM is mutating, trigger/function may not see it
ORA-06512: at "BOB.TRG_INS_JIM_AIR", line 7
ORA-04088: error during execution of trigger 'BOB.TRG_INS_JIM_AIR'
Same result a row level trigger CONNOT refer to the table that caused it to fire
*/
--- How about an AUTONOMOUS_TRANSACTION;
alter trigger trg_ins_jim_air disable;
alter trigger trg_ins_jim_birat enable;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
-- 14 rows inserted
select 'Jim', count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 14
(looks promising) so continue
*/
commit ;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
-- 14 rows inserted
select 'Jim', count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 28
Results 28
NO that allows 28 rows in table.
Well not because the trigger isn't working but because it's an AUTONOMOUS TRANSACTION. (which cannot see the rows processed outside of it)
But is it because we used a BEFORE Trigger?
*/
rollback ;
select 'Jim', count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 28
HOW? The rollback above rolls back the second set of rows in jim, but NOT results as they were committed
during to the requirements of AUTONOMOUS TRANSACTION.
*/
delete from jim;
delete from results;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
-- 14 rows inserted
commit ;
alter trigger trg_ins_jim_birat disable;
alter trigger trg_ins_jim_airat enable;
select 'Jim', count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 14
*/
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
-- 14 rows inserted
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 28
Results 28
Same results for after trigger as before with AUTONOMOUS TRANSACTION.
*/
rollback;
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 28
And same results after rollback;
*/
delete from jim;
delete from results;
commit ;
alter trigger trg_ins_jim_airat disable;
alter trigger trg_ins_jim_ais enable;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
-- 14 rows inserted
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 0
OOPS: Nothing in results.
*/
rollback;
alter trigger trg_ins_jim_airat enable;
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
-- 14 rows inserted
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 14
*/
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15;
/*
Error starting at line : 266 in command -
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15
Error report -
ORA-20347: Too many records
ORA-06512: at "BOB.TRG_INS_JIM_AIS", line 6
ORA-04088: error during execution of trigger 'BOB.TRG_INS_JIM_AIS'
But is it really?
*/
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 14
Results 28
Well maybe not!!
*/
delete from jim;
delete from results;
commit ;
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 0
Results 0
*/
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15000;
/*
insert into jim (id, age, price)
select level, trunc(dbms_random.value(10,30)), round(50*dbms_random.value,2)
from dual connect by level < 15000
Error report -
ORA-20347: Too many records
ORA-06512: at "BOB.TRG_INS_JIM_AIS", line 6
ORA-04088: error during execution of trigger 'BOB.TRG_INS_JIM_AIS'
*/
select 'Jim' tbl, count(*) cnt from jim
union all
select 'Results', count(*) from results;
/*
Jim 0
Results 14999
Is this really what you want?
*/
这个故事的寓意是:以这样或那样的方式在应用程序中强制执行此规则,而不是数据库触发器。这里的每件事都是一次会议。多个会话会使问题变得更加复杂 即使触发器起作用,您也可以在表中获得16条以上的记录。如果表已经有15条记录,请打开两个数据库会话,让它们插入一条记录,但不要提交。触发器将计数15条记录,因为它不会在另一个会话中看到未限制的插入,并且将允许插入。100%true。我考虑在回答时指出这一点,但上面提到的问题似乎更像是一个练习,而不是一个现实世界的问题,所以我把它省略了,以保持事情的简单。我很高兴你提起这件事。