2009-11-16 11 views
17

Penso che questo è un problema abbastanza comune.Come scrivere un vincolo riguardante un numero massimo di righe in postgresql?

Ho un tavolo e un tavolo user(id INT ...)photo(id BIGINT, owner INT). il proprietario è un riferimento su user(id).

vorrei aggiungere un vincolo alla foto tavolo che impedirebbe più di diciamo 10 foto per accedere al database per ogni utente.

Qual è il modo migliore di scrivere questo?

Thx!

risposta

18

Quassnoi è giusto; un trigger sarebbe il modo migliore per raggiungere questo obiettivo.

Ecco il codice:

CREATE OR REPLACE FUNCTION enforce_photo_count() RETURNS trigger AS $$ 
DECLARE 
    max_photo_count INTEGER := 10; 
    photo_count INTEGER := 0; 
    must_check BOOLEAN := false; 
BEGIN 
    IF TG_OP = 'INSERT' THEN 
     must_check := true; 
    END IF; 

    IF TG_OP = 'UPDATE' THEN 
     IF (NEW.owner != OLD.owner) THEN 
      must_check := true; 
     END IF; 
    END IF; 

    IF must_check THEN 
     -- prevent concurrent inserts from multiple transactions 
     LOCK TABLE photos IN EXCLUSIVE MODE; 

     SELECT INTO photo_count COUNT(*) 
     FROM photos 
     WHERE owner = NEW.owner; 

     IF photo_count >= max_photo_count THEN 
      RAISE EXCEPTION 'Cannot insert more than % photos for each user.', max_photo_count; 
     END IF; 
    END IF; 

    RETURN NEW; 
END; 
$$ LANGUAGE plpgsql; 


CREATE TRIGGER enforce_photo_count 
    BEFORE INSERT OR UPDATE ON photos 
    FOR EACH ROW EXECUTE PROCEDURE enforce_photo_count(); 

ho inserito tabella di bloccaggio al fine di evitare situazioni in cui due tansactions simultanee conterebbe foto per un utente, vedere che il conteggio attuale è 1 sotto del limite, e quindi entrambi inserto , che ti farebbe andare 1 oltre il limite. Se questo non è un problema per te, sarebbe meglio rimuovere il blocco in quanto può diventare un collo di bottiglia con molti inserti/aggiornamenti.

+0

Un'altra cosa: sfortunatamente, tutte le istruzioni IF all'inizio del trigger non possono essere unite in un singolo "IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' E NEW.owner! = OLD.owner) THEN ... "perché PLPGSQL non supporta i cortocircuiti. –

7

Non si può scrivere un tale vincolo in una dichiarazione tavolo.

ci sono alcune soluzioni:

  • creare un trigger che avrebbe controllare il numero di foto per ogni utente
  • creare una colonna che avrebbe mantenuto l'ordine delle foto, fare (user_id, photo_order)UNIQUE, e aggiungere CHECK(photo_order BETWEEN 1 AND 10)
+1

Non dovresti quindi imporre l'unicità sull'ordine delle foto? Come farlo? – ahnbizcad

2

Un altro approccio sarebbe quello di aggiungere la colonna "photo_count" alla tabella degli utenti, aggiornarla con i trigger per far sì che riflettano la realtà e aggiungere il controllo su di essa per imporre il numero massimo di foto.

Il vantaggio di questo è che in qualsiasi momento si sa (senza contare) quante foto ha l'utente.

D'altra parte, l'approccio suggerito da Quassnoi è anche piuttosto interessante, in quanto consente di riordinare le foto nel caso in cui l'utente lo desiderasse.

Problemi correlati