我认为这是一个非常普遍的问题。
我有一张桌子user(id INT ...)
和一张桌子photo(id BIGINT, owner INT)
。所有者是user(id)
的参考。
我想在桌面照片中添加一个约束,这样可以防止让每个用户输入10张照片进入数据库。
写这个的最好方法是什么?
谢谢!
Quassnoi是对的;触发器是实现这一目标的最佳方式。
这是代码:
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。如果您不关心这一点,最好删除锁定,因为它可能成为许多插入/更新的瓶颈。
您不能在表声明中编写这样的约束。
有一些解决方法:
photo_order
列,保持照片的顺序,使(user_id, photo_order)
UNIQUE
,并添加CHECK(photo_order BETWEEN 1 AND 10)
另一种方法是将“photo_count”列添加到users表,使用触发器更新它以使其反映现实,并添加检查以强制执行最大数量的照片。
这样做的另一个好处是,在任何特定时刻,我们都知道(不计算)用户拥有的照片数量。
另一方面 - Quassnoi建议的方法也非常酷,因为它使您能够在用户需要的情况下重新排序照片。
更好的选择是在执行插入时检查行数:
insert into photos(id,owner)
select 1,2 from dual
where (select count(*) from photos where id=1) < 10