计算触发器更新前要受影响的行数

时间:2010-04-01 13:41:26

标签: postgresql

我想知道UPDATE每个语句触发器中BEFORE查询会影响的行数。这可能吗?

问题是我想只允许最多更新4行的查询。如果受影响的行数为5或更多,我想提出错误。

我不想在代码中执行此操作,因为我需要对数据库级别进行此检查。 这有可能吗?

提前感谢有关该

的任何线索

6 个答案:

答案 0 :(得分:2)

编写一个为您更新行或执行回滚的函数。抱歉格式不佳。

create function update_max(varchar, int)
RETURNS void AS
$BODY$

DECLARE

   sql ALIAS FOR $1;
   max ALIAS FOR $2;
   rcount INT;

BEGIN

   EXECUTE sql;
   GET DIAGNOSTICS rcount = ROW_COUNT;

   IF rcount > max THEN

       --ROLLBACK;
       RAISE EXCEPTION 'Too much rows affected (%).', rcount;

   END IF;

   --COMMIT;

END;

$BODY$ LANGUAGE plpgsql

然后将其称为

select update_max('update t1 set id=id+10 where id < 4', 3);

第一个参数是你的sql-Statement,第二个是你的最大行。

答案 1 :(得分:2)

Simon有一个好主意,但他的实施不必要地复杂化。这是我的主张:

create or replace function trg_check_max_4()                
returns trigger as $$
begin
        perform true from pg_class 
                where relname='check_max_4' and relnamespace=pg_my_temp_schema(); 
        if not FOUND then
                create temporary table check_max_4 
                        (value int check (value<=4)) 
                        on commit drop;
                insert into check_max_4 values (0); 
        end if;

        update check_max_4 set value=value+1; 
        return new;
end; $$ language plpgsql;

答案 2 :(得分:1)

PostgreSQL有两个types of triggers:行和语句触发器。行触发器仅在行的上下文中工作,因此您无法使用它们。不幸的是,“之前”声明会触发don't see即将发生什么样的变化,所以我不相信你也可以使用它们。

基于此,我会说你不太可能使用触发器在数据库中构建这种保护,除非你不介意使用“after”触发器并在条件下回滚事务不满意。不介意被证明是错的。 :)

答案 3 :(得分:1)

查看使用Serializable Isolation Level。我相信这将为您提供事务中数据库数据的一致视图。然后你可以使用MusiGenesis提到的选项#1,没有时间漏洞。当然要测试它来验证。

答案 4 :(得分:1)

我创造了这样的东西:

begin;

create table test (
    id integer
);

insert into test(id) select generate_series(1,100);


create or replace function trg_check_max_4_updated_records() 
returns trigger as $$
declare
    counter_ integer := 0;
    tablename_ text := 'temptable';
begin
    raise notice 'trigger fired';
    select count(42) into counter_ 
        from pg_catalog.pg_tables where tablename = tablename_;
    if counter_ = 0 then
        raise notice 'Creating table %', tablename_;
        execute 'create temporary table ' || tablename_ || ' (counter integer) on commit drop';
        execute 'insert into ' || tablename_ || ' (counter) values(1)';

        execute 'select counter from ' || tablename_ into counter_;
        raise notice 'Actual value for counter= [%]', counter_;
    else
        execute 'select counter from ' || tablename_ into counter_;
        execute 'update ' || tablename_ || ' set counter = counter + 1';
        raise notice 'updating';
        execute 'select counter from ' || tablename_ into counter_;
        raise notice 'Actual value for counter= [%]', counter_;

        if counter_ > 4 then
            raise exception 'Cannot change more than 4 rows in one trancation';
        end if;

    end if;
    return new;
end; $$ language plpgsql;


create trigger trg_bu_test before 
  update on test 
  for each row
  execute procedure trg_check_max_4_updated_records();

update test set id = 10 where id <= 1;
update test set id = 10 where id <= 2;
update test set id = 10 where id <= 3;
update test set id = 10 where id <= 4;
update test set id = 10 where id <= 5;

rollback;

主要思想是在“每行更新之前”触发,创建(如果需要)临时表(在事务结束时删除)。在此表中只有一行具有一个值,即当前事务中更新的行数。对于每次更新,值都会递增。如果该值大于4,则交易将停止。

但我认为这对你的问题是一个错误的解决方案。运行这样一个错误的查询,你写了两次,这是一个问题,所以你将改变8行。如何删除行或截断它们?

答案 5 :(得分:0)

我从未使用过postgresql,所以我的回答可能不适用。在SQL Server中,您的触发器可以调用存储过程,该存储过程可以执行以下两种操作之一:

  1. 执行SELECT COUNT(*)以确定将受UPDATE影响的记录数,然后仅在计数为4或更少时执行UPDATE
  2. 在事务中执行UPDATE,并且只有在返回的受影响行数为4或更少时才提交事务
  3. 没有。 1是时间易受攻击的(受UPDATE影响的记录数可能在COUNT(*)检查和实际UPDATE之间发生变化。如果有很多情况下更新的行数大于4,则No.2效率非常低。