【问题标题】:Function to update a status flag for validity of other column?更新状态标志以确保其他列的有效性的功能?
【发布时间】:2015-10-13 19:47:36
【问题描述】:

如何创建一个比较 card_idallowed_cards 数组元素的函数?
如果在此数组中,则列状态必须更新为TRUE

CREATE TABLE client_1 (
  id bigint NOT NULL,
  "time" timestamp without time zone DEFAULT now(),
  status boolean,
  card_id character varying(10),
  CONSTRAINT client_1_pkey PRIMARY KEY (id)
);

CREATE TABLE allowed_cards (
  allowed_cards character varying(10)[]
);

INSERT INTO allowed_cards VALUES ('{DD3234,FF2342}');

【问题讨论】:

标签: sql postgresql database-design referential-integrity check-constraints


【解决方案1】:

始终强制执行有效的card_id

首先,如果您需要表 card_allowed,请使用普通的 varchartext 列(包含多个条目),而不是数组:

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

【讨论】:

    猜你喜欢
    • 2019-04-18
    • 1970-01-01
    • 2021-04-24
    • 1970-01-01
    • 1970-01-01
    • 2023-02-09
    • 1970-01-01
    • 2021-11-02
    • 2020-04-16
    相关资源
    最近更新 更多