问题
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 , which is a reserved word in standard SQL and also very misleading for an actual time
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