始终强制执行有效的card_id
首先,如果您需要表 card_allowed,请使用普通的 varchar 或 text 列(包含多个条目),而不是数组:
CREATE TABLE card_allowed (
card_id varchar(10) NOT NULL PRIMARY KEY
);
INSERT INTO card_allowed VALUES ('DD3234'), ('FF2342');
接下来,为了强制有效卡片,您现在可以使用FK constraint:
CREATE TABLE client_1 (
client_1_id bigint NOT NULL PRIMARY KEY,
tstz timestamptz DEFAULT now(),
-- card_valid boolean,
card_id varchar(10),
CONSTRAINT client_1_card_id_fk FOREIGN KEY (card_id) REFERENCES card_allowed
);
由于该列可以为 NULL,因此如果您没有有效的 card_id,则始终可以将其留空。
您不需要额外的列(您将其命名为 status,我将其重命名为 card_valid)。
我还更改了一些列名以使其更有用。除此之外,我使用列名tstz 而不是time,这是标准SQL 中的保留字,对于实际的timestamptz 列也非常误导。
如果您需要在card_id 中允许无效值(真的吗?),FK 约束是不可能的。还有其他选择:
只检查新条目
您可以“伪造”运行检查的IMMUTABLE 函数:
CREATE OR REPLACE FUNCTION f_card_allowed(text)
RETURNS bool AS
$func$
SELECT EXISTS (SELECT 1 FROM card_allowed WHERE card_allowed = $1);
$func$
LANGUAGE sql STABLE; -- not actually IMMUTABLE
函数真的不是不可变的,因为它依赖于另一个表中的值。所以实际上只有STABLE。同一调用的结果可以在事务之间改变。根据定义 CHECK constraints 期望 IMMUTABLE 函数,但允许一些余地(尤其是时间函数)STABLE 是可以容忍的。不过,您应该将 CHECK 约束标记为 NOT VALID 以记录这一点:
ALTER TABLE client_1 ADD CONSTRAINT client_1_card_allowed
CHECK (f_card_allowed(card_id)) NOT VALID;
区别:不强制参考完整性,就像 FK 约束一样。仅在插入/更新时检查行,并且此时必须有效。没有对现有行的状态做出任何承诺:您现在可能已经更改了 card_allowed 中的值。详情:
设置状态一次
现在您需要我在上面的表格定义中注释掉的附加标志card_valid。您有没有 FK 约束:
UPDATE client_1 c
SET card_valid = EXISTS (SELECT 1 FROM card_allowed WHERE card_id = c.card_id);
每次更改后设置状态
您可以在触发器函数中对每个插入/更新的行执行相同操作:
CREATE OR REPLACE FUNCTION trg_client_1_insupbef()
RETURNS trigger AS
$BODY$
BEGIN
NEW.card_valid := EXISTS (SELECT 1 FROM card_allowed WHERE card_id = NEW.card_id);
RETURN NEW;
END
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER insupbef
BEFORE INSERT OR UPDATE ON client_1
FOR EACH ROW EXECUTE PROCEDURE trg_client_1_insupbef();