2010-04-01 26 views
8

Tôi muốn biết số hàng sẽ bị ảnh hưởng bởi UPDATE truy vấn trong BEFORE mỗi lần kích hoạt câu lệnh. Điều đó có thể không?đếm số hàng bị ảnh hưởng trước khi cập nhật trong trình kích hoạt

Vấn đề là tôi chỉ muốn cho phép các truy vấn cập nhật tối đa 4 hàng. Nếu số hàng bị ảnh hưởng là 5 hoặc nhiều hơn tôi muốn tăng lỗi.

Tôi không muốn làm điều này trong mã vì tôi cần kiểm tra này ở cấp độ db. Điều này có thể thực hiện được không?

Cảm ơn trước cho bất kỳ manh mối về mà

+0

Tôi xem xét sử dụng truy vấn bình thường + rollback nếu hàng bị ảnh hưởng lớn hơn max và dường như có giá trị tốt nhất như tôi thấy. Vâng trong 99% + sẽ có các truy vấn tốt (4 hàng được cập nhật tối đa) nhưng đây chỉ là bảo mật bổ sung cho hệ thống. Bảng với 'vấn đề' này là khá lớn và quan trọng đối với hệ thống để khôi phục lại nó sau khi truy vấn sai lầm như vậy có thể là đau đớn cho tất cả mọi người. Cảm ơn tất cả các bạn đã trả lời. Không biết cái nào để chấp nhận bởi vì tất cả chúng đều hữu ích :) – sbczk

+0

Tại sao bạn muốn làm điều đó? Có lẽ có nhiều cách dễ dàng hơn để làm điều đó hơn một truy vấn lạ. Còn gì nữa ... sử dụng số đếm (nếu có thể) sẽ chậm hơn trong khi bảng phát triển. –

+0

Vì vậy, bạn nghĩ rằng khi bạn có tối đa 4 hàng thay đổi truy vấn không thể được nhập sai? Điều này nghe giống như một loại cảm giác an toàn giả. –

Trả lời

1

tôi đã tạo ra một cái gì đó như thế này:

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; 

Ý tưởng chính là phải có một kích hoạt trên 'trước bản cập nhật cho mỗi hàng' tạo (nếu cần) một bảng tạm thời (có nghĩa là giảm tại kết thúc giao dịch). Trong bảng này chỉ có một hàng với một giá trị, đó là số hàng được cập nhật trong giao dịch hiện tại. Đối với mỗi cập nhật, giá trị được tăng lên. Nếu giá trị lớn hơn 4, giao dịch sẽ bị dừng.

Nhưng tôi nghĩ rằng đây là giải pháp sai cho vấn đề của bạn. Có vấn đề gì khi chạy truy vấn sai mà bạn đã viết về, hai lần, vì vậy bạn sẽ có 8 hàng thay đổi. Điều gì về xóa hàng hoặc cắt xén chúng?

0

Tôi chưa bao giờ làm việc với postgresql, vì vậy câu trả lời của tôi có thể không áp dụng. Trong SQL Server, trình kích hoạt của bạn có thể gọi thủ tục được lưu trữ có thể thực hiện một trong hai điều sau:

  1. Thực hiện SELECT COUNT (*) để xác định số lượng hồ sơ sẽ bị ảnh hưởng bởi UPDATE, và sau đó chỉ thực hiện UPDATE nếu tính là 4 hoặc ít hơn
  2. tiến hành cập nhật trong một giao dịch, và chỉ cam kết giao dịch nếu số trở hàng bị ảnh hưởng là 4 hoặc ít hơn

số 1 là thời gian dễ bị tổn thương (số hồ sơ bị ảnh hưởng bởi UPDATE có thể thay đổi giữa COUNT (*) kiểm tra và UPDATE thực tế. Số 2 là khá kém hiệu quả, nếu có nhiều ca ses nơi số lượng hàng được cập nhật lớn hơn 4.

1

PostgreSQL có hai types of triggers: trình kích hoạt hàng và câu lệnh. Trình kích hoạt hàng chỉ hoạt động trong ngữ cảnh của một hàng để bạn không thể sử dụng những thứ đó. Thật không may, câu lệnh "trước khi" kích hoạt don't see loại thay đổi sắp diễn ra nên tôi không tin bạn cũng có thể sử dụng chúng. Dựa trên điều đó, tôi có thể nói rằng bạn không thể xây dựng loại bảo vệ đó vào cơ sở dữ liệu bằng trình kích hoạt, trừ khi bạn không ngại sử dụng trình kích hoạt "sau" và quay lại giao dịch nếu điều kiện không hài lòng. Sẽ không nhớ được chứng minh sai. :)

1

Hãy xem bằng cách sử dụng Cấp cách ly nối tiếp. Tôi tin rằng điều này sẽ cung cấp cho bạn một cái nhìn nhất quán về dữ liệu cơ sở dữ liệu trong giao dịch của bạn. Sau đó, bạn có thể sử dụng tùy chọn # 1 mà MusiGenesis đã đề cập, mà không có lỗ hổng thời gian. Kiểm tra nó tất nhiên để xác nhận.

+0

Đúng, cách tốt nhất để thả hiệu quả cơ sở dữ liệu như tất cả các giao dịch sẽ được thực hiện từng cái một :( –

2

Viết hàm cập nhật các hàng cho bạn hoặc thực hiện khôi phục. Xin lỗi vì định dạng kiểu kém.

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 

Sau đó gọi nó như

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

nơi param đầu tiên ist sql-Tuyên Bố của bạn và các hàng tối đa của bạn 2.

2

Simon có ý tưởng hay nhưng việc triển khai của anh ấy phức tạp một cách không cần thiết. Đây là đề xuất của tôi:

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; 
Các vấn đề liên quan