问题描述
我有两张桌子,一张叫选票,一张叫票。选票存储一个字符串列表,这些字符串代表人们可以投票的选项:
CREATE TABLE IF NOT EXISTS Polls (
id SERIAL PRIMARY KEY,options text[]
);
Votes存储用户做出的投票(其中的投票是一个整数,代表他们投票的选项的索引):
CREATE TABLE IF NOT EXISTS Votes (
id SERIAL PRIMARY KEY,poll_id integer references Polls(id),value integer NOT NULL,cast_by integer NOT NULL
);
我想确保每当在Votes表中创建一行时,“ value”的值在民意调查中相应行的范围[0,length(options))内(相应地,我的意思是该行poll_id匹配的地方。
是否可以实施任何形式的检查或外键约束?还是我需要某种触发?如果是这样,那触发将是什么样子,并且会存在性能问题?仅使用SELECT语句手动查询相应的民意调查,然后断言“值”在插入投票表中之前是否有效?
解决方法
您不能使用“检查约束”,因为它可以引用同一表的列。
您可以参考官方Manual。
因此,这里您应该在BEFORE INSERT
表的Votes
事件上使用Trigger,或者可以对插入操作使用function / procedure(取决于PostgreSQL的版本)来检查值如果不满足条件,则在插入之前引发异常。
使用触发器:
create or replace function id_exist() returns trigger as
$$
begin
if new.value<0 or new.value>=(select array_length(options,1) from polls where id=new.poll_id) then
raise exception 'value is not in range';
end if;
return new;
end;
$$
language plpgsql
CREATE TRIGGER check_value BEFORE INSERT ON votes
FOR EACH ROW EXECUTE PROCEDURE id_exist();
,
我建议您修改数据模型以包含一个表PollOptions
:
CREATE TABLE IF NOT EXISTS PollOptions (
PollOptionsId SERIAL PRIMARY KEY,-- should use generated always as identity
PollId INT NOT NULL,REFERENCES Polls(id),OptionNumber int,Option text,UNIQUE (PollId,Option)
);
然后,您的Votes
表应该具有对PollOptions
的外键引用。您可以使用PollOptionId
或(PollId,Option)
。
如果正确设置数据,则无需触发器或特殊功能。