2010-04-01 5 views
8

Ich möchte die Anzahl der Zeilen kennen, die von UPDATE Abfrage in BEFORE pro Anweisung ausgelöst werden. Ist das möglich?Anzahl der Zeilen, die vor der Aktualisierung im Trigger betroffen sein müssen

Das Problem ist, dass ich nur Abfragen zulassen möchte, die bis zu 4 Zeilen aktualisieren. Wenn die Anzahl der betroffenen Zeilen 5 oder mehr beträgt, möchte ich den Fehler erhöhen.

Ich möchte dies nicht im Code tun, weil ich diese Überprüfung auf Db-Ebene brauche. Ist das überhaupt möglich?

Vielen Dank im Voraus für alle Hinweise auf diesen

+0

Ich dachte über normale Abfrage + Rollback, wenn betroffene Zeilen größer als max ist, und das scheint der beste Wert, wie ich sehe. Nun, in 99% + wird es gute Abfragen geben (4 aktualisierte Zeilen maximal), aber dies ist nur zusätzliche Sicherheit für das System. Die Tabelle mit diesem "Problem" ist ziemlich groß und kritisch für das System, so dass die Wiederherstellung nach einer solchen verfälschten Abfrage für alle schmerzhaft sein kann. Vielen Dank für die Antworten. Ich weiß nicht, welches man annehmen soll, weil alle hilfreich waren :) – sbczk

+0

Warum willst du das machen? Vielleicht gibt es einen viel einfacheren Weg als eine so seltsame Abfrage. Was mehr ... Die Verwendung von count (wenn möglich) wird langsamer, während die Tabelle wächst. –

+0

Sie denken also, dass wenn Sie maximal 4 Zeilen geändert haben, die Abfrage nicht falsch eingegeben werden konnte? Das klingt nach einer Art falschem Sicherheitsgefühl. –

Antwort

1

Ich habe so etwas wie diese erstellt:

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; 

Die Hauptidee ist es, einen Auslöser haben, auf ‚vor der Aktualisierung für jede Reihe‘, die (falls erforderlich) eine temporäre Tabelle erstellt (das bei dem abfall Ende der Transaktion). In dieser Tabelle gibt es nur eine Zeile mit einem Wert, das ist die Anzahl der aktualisierten Zeilen in der aktuellen Transaktion. Für jede Aktualisierung wird der Wert erhöht. Wenn der Wert größer als 4 ist, wird die Transaktion gestoppt.

Aber ich denke, dass dies eine falsche Lösung für Ihr Problem ist. Was ist ein Problem beim Ausführen einer solchen falschen Abfrage, über die Sie schon zweimal geschrieben haben, so dass Sie 8 Zeilen geändert haben? Was ist mit Löschzeilen oder deren Kürzung?

0

habe ich noch nie mit postgresql gearbeitet, so kann meine Antwort nicht gelten. In SQL Server können Sie Ihre Trigger eine gespeicherte Prozedur aufrufen, die eines von zwei Dingen tun würde:

  1. Führen Sie eine SELECT COUNT (*) die Anzahl der Datensätze zu bestimmen, die durch das Update betroffen sein werden, und auch nur dann ausführen wenn die Aktualisierung der Zählstand 4 oder weniger
  2. die Aktualisierung durchführen innerhalb einer Transaktion, und nur die Transaktion verpflichten, wenn die zurückgegebene Anzahl der betroffenen Zeilen ist 4 oder weniger

No. 1 ist das Timing verwundbar (die Anzahl der von der UPDATE betroffenen Datensätze kann sich zwischen der COUNT (*) - Prüfung und der tatsächlichen UPDATE ändern, Nr. 2 ist ziemlich ineffizient, wenn es viele ca ses wobei die Anzahl der aktualisierten Zeilen größer als 4 ist.

1

PostgreSQL hat zwei types of triggers: Zeilen- und Anweisungsauslöser. Zeilentrigger funktionieren nur im Kontext einer Zeile, so dass Sie diese nicht verwenden können. Leider löst die "Vorher" -Anweisung don't see aus, welche Art von Änderung stattfindet, also glaube ich nicht, dass Sie diese auch verwenden können.

Darauf basierend würde ich sagen, es ist unwahrscheinlich, dass Sie in der Lage sein werden, diese Art von Schutz in die Datenbank mit Triggern zu erstellen, es sei denn, Sie haben nichts dagegen, einen "nach" Trigger zu verwenden und die Transaktion rückgängig zu machen Bedingung ist nicht erfüllt. Würde nichts dagegen haben, sich als falsch erwiesen zu haben. :)

1

Werfen Sie einen Blick auf die Verwendung von Serializable Isolation Level. Ich glaube, dies wird Ihnen eine konsistente Sicht auf die Datenbankdaten innerhalb Ihrer Transaktion geben. Dann können Sie die Option 1, die MusiGenesis erwähnt, ohne die Timing-Schwachstelle verwenden. Testen Sie es natürlich, um es zu validieren.

+0

Ja, der beste Weg zu fallen die Datenbank-Effizienz, da alle Transaktionen eins nach dem anderen erledigt werden würden :( –

2

Schreiben Sie eine Funktion, die die Zeilen für Sie aktualisiert oder einen Rollback durchführt. Sorry für schlechte Formatierung.

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 

es dann nennen wie

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

, wo der erste param ist der SQL-Statement und die 2. Ihr max Reihen.

2

Simon hatte eine gute Idee, aber seine Implementierung ist unnötig kompliziert. Dies ist mein Vorschlag:

Verwandte Themen