Function to update a status flag for validity of other column?

喜欢而已 提交于 2019-12-12 02:25:42

问题


How to create a function that compares card_id with allowed_cards array elements?
If it's in this array, column status must be updated to 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}');

回答1:


Enforce valid card_id at all times

First of all, if you need the table card_allowed, use a plain varchar or text column (with multiple entries), not an array:

CREATE TABLE card_allowed (
  card_id varchar(10) NOT NULL PRIMARY KEY
);

INSERT INTO card_allowed VALUES ('DD3234'), ('FF2342');

Next, to enforce valid cards, you could now just use a 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
);

Since the column can be NULL, you can always leave it empty if you don't have a valid card_id.

You don't need an additional column (you named it status, I renamed it card_valid).

I also changed some column names to make it more useful. Among other things, I am using the column name tstz instead of time, which is a reserved word in standard SQL and also very misleading for an actual timestamptz column.


If you need to allow invalid values in card_id (really?), an FK constraint is not possible. There are other options:

Only check new entries

You can "fake" an IMMUTABLE function that runs the check:

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

The function is not really immutable, because it depends on values from another table. So it is actually only STABLE. The result of the same call can change between transactions. By definition CHECK constraints expect IMMUTABLE functions, but to allow some leeway (especially with temporal functions) STABLE is tolerated. You should mark the CHECK constraint as NOT VALID to document this, though:

ALTER TABLE client_1 ADD CONSTRAINT client_1_card_allowed
CHECK (f_card_allowed(card_id)) NOT VALID;

The difference: Referential integrity is not enforced at all times like with the FK constraint. Rows are only checked when inserted / updated and must be valid at this time. No promises are made as to the status of existing rows: you might have changed values in card_allowed by now. Details:

  • CONSTRAINT to check values from a remotely related table (via join etc.)

Set the status once

Now you need the additional flag card_valid I commented out in the table definition above. You have no FK constraint:

UPDATE client_1 c
SET    card_valid = EXISTS (SELECT 1 FROM card_allowed WHERE card_id = c.card_id);

Set the status after every change

You could do the same in a trigger function for every inserted / updated row:

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


来源:https://stackoverflow.com/questions/31588480/function-to-update-a-status-flag-for-validity-of-other-column

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!