PostgreSQL - 基于另一个列的约束 Table

PostgreSQL - Constraint Based on Column in Another Table

我有两个table,一个叫ballots,一个叫votes。选票存储代表人们可以投票的选项的字符串列表:

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

我想确保每当在投票 table 中创建一行时,'value' 的值在民意调查中相应行的 [0,length(options)) 范围内(通过对应,我指的是 poll_id 匹配的行)。

我可以实施任何类型的检查或外键约束来完成此任务吗?或者我需要某种触发器吗?如果是这样,触发器会是什么样子,会不会有性能问题?使用 SELECT 语句手动查询相应的民意调查,然后断言 'value' 在插入 Votes table 之前有效,是否同样高效?

在您的要求中,您不能使用检查约束,因为它可以引用相同的列 table。

同样可以参考官方Manual

所以,在这里你应该在 Votes Table 的 BEFORE INSERT 事件上使用触发器,或者你可以使用 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();

DEMO

我建议您将数据模型修改为 table、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 table 应该有一个指向 PollOptions 的外键引用。您可以使用 PollOptionId(PollId, Option).

如果正确设置数据,则不需要触发器或特殊函数。