2009-11-16 3 views
17

Я думаю, что это довольно распространенная проблема.Как написать ограничение относительно максимального количества строк в postgresql?

У меня есть стол user(id INT ...) и стол photo(id BIGINT, owner INT). владелец является ссылкой на user(id).

Я хотел бы добавить ограничение на фотографию стола, которое предотвратит более чем 10 фотографий для входа в базу данных для каждого пользователя.

Каков наилучший способ написать это?

Thx!

ответ

18

Quassnoi is right; триггер будет лучшим способом для достижения этого.

Вот код:

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(); 

я включил таблицу блокировки для того, чтобы избежать ситуаций, когда два параллельных tansactions бы сосчитать фотографии для пользователя, видеть, что текущий отсчет 1 ниже предела, а затем и вставка , что приведет к тому, что вы перейдете 1 за пределы. Если это вас не волнует, было бы лучше удалить блокировку, поскольку она может стать узким местом со многими вставками/обновлениями.

+0

Еще одна вещь: к сожалению, все операторы IF в начале триггера не могут быть объединены в один «IF TG_OP =« INSERT »OR (TG_OP =« UPDATE »и NEW.owner! = OLD.owner) THEN ... ", потому что PLPGSQL не поддерживает короткое замыкание. –

7

Вы не можете написать такое ограничение в объявлении таблицы.

Есть некоторые обходные пути:

  • Создать триггер, который будет проверять количество фотографий для каждого пользователя
  • Создайте столбец, который будет держать порядок фотографий, сделать (user_id, photo_order)UNIQUE и добавить CHECK(photo_order BETWEEN 1 AND 10)
+1

Не могли бы вы последовать за уникальностью при заказе фотографии? Как это сделать? – ahnbizcad

2

Другим подходом было бы добавить столбец «photo_count» в таблицу пользователей, обновить его с помощью триггеров, чтобы он отражал реальность, и добавить проверку на него, чтобы обеспечить максимальное количество фотографий.

Боковое преимущество заключается в том, что в любой момент мы знаем (без учета), сколько у пользователя фотографий.

С другой стороны, подход Quassnoi также очень крут, так как он дает вам возможность изменять порядок фотографий в случае, если пользователь захочет его.

Смежные вопросы