我有表tariffs
,有两列:(tariff_id, reception)
我有表users
,有两列:(user_id, reception)
我的表users_tariffs
有两列:(user_id, tariff_id)
。
我希望防止来自一个接收的资费从另一个接收分配给用户的情况。我怎么能这样做?
E.G
用户:
user_id | reception
Putin | Russia
Trump | USA
关税:
tariff_id | reception
cheap | USA
expensive | Russia
users_tariffs的错误情况,因为廉价关税只适用于美国:
user_id | tariff_id
Putin | Cheap
答案 0 :(得分:1)
解决方案1:外部关键约束
我假设以下表定义。
特别是,user_tariffs
中的复合键使users
和tariffs
之间成为多对多关系。
CREATE TABLE tariffs (tariff_id int NOT NULL PRIMARY KEY,
reception text NOT NULL);
CREATE TABLE users (user_id int NOT NULL PRIMARY KEY,
reception text NOT NULL);
CREATE TABLE user_tariffs (tariff_id int NOT NULL REFERENCES tariffs (tariff_id),
user_id int NOT NULL REFERENCES users (user_id),
PRIMARY KEY (tariff_id, user_id));
您可能需要将所有三个列组合在一起,所以让我们创建它:
ALTER TABLE user_tariffs ADD COLUMN reception text;
UPDATE user_tariffs a
SET reception = b.reception
FROM (SELECT * FROM tariffs) b
WHERE a.tariff_id = b.tariff_id;
ALTER TABLE user_tariffs ALTER COLUMN reception SET NOT NULL;
现在我们可以在(user_id, reception)
中使用FOREIGN KEY REFERENCES users
。
CREATE UNIQUE INDEX ON tariffs (tariff_id, reception);
ALTER TABLE user_tariffs ADD FOREIGN KEY (tariff_id, reception)
REFERENCES tariffs (tariff_id, reception);
此外,我们可以将{FK REF (tariff_id, reception)
用于tariffs
。
CREATE UNIQUE INDEX ON users (user_id, reception);
ALTER TABLE user_tariffs ADD FOREIGN KEY (user_id, reception)
REFERENCES users (user_id, reception);
填充数据:
INSERT INTO users VALUES (1, 'cheap'), (2, 'expensive');
INSERT INTO tariffs VALUES (1, 'cheap'), (2, 'expensive');
现在假设我们要插入以下数据(user_id, tariff_id)
:
WITH data (user_id, tariff_id)
AS (VALUES (1, 2), (2, 1)), -- here is your application data
datas (user_id, tariff_id, reception)
AS (SELECT user_id,
tariff_id,
(SELECT u.reception -- reception calculated by user
FROM users u
WHERE u.user_id = d.user_id)
FROM data d)
INSERT INTO user_tariffs SELECT * FROM datas ;
然后您无法插入数据,因为您只能使用相同的(1, 1)
添加(2, 2)
或reception
,而不能添加(1, 2)
或(2, 1)
reception
'第错误消息是:
ERROR: insert or update on table "user_tariffs" violates foreign key constraint "user_tariffs_user_id_fkey1"
DETAIL: Key (user_id, reception)=(2, cheap) is not present in table "users".
但您可以使用data AS VALUES (1, 1), (2, 2)
插入。
我认为FOREIGN KEY CONSTRAINT解决方案是首选。
如果您想要更好的桌面设计,请描述您的functional dependencies
。
解决方案2:TRIGGER
-- DROP TABLE user_tariffs CASCADE;
-- DROP TABLE users CASCADE;
-- DROP TABLE tariffs CASCADE;
CREATE TABLE tariffs (tariff_id int NOT NULL PRIMARY KEY,
reception text NOT NULL);
CREATE TABLE users (user_id int NOT NULL PRIMARY KEY,
reception text NOT NULL);
CREATE TABLE user_tariffs (tariff_id int NOT NULL REFERENCES tariffs (tariff_id),
user_id int NOT NULL REFERENCES users (user_id),
PRIMARY KEY (tariff_id, user_id));
INSERT INTO users VALUES (1, 'cheap'), (2, 'expensive');
INSERT INTO tariffs VALUES (1, 'cheap'), (2, 'expensive');
-- table user_tariffs (user_id, tariff_id) only, without reception column.
创建一个返回类型为trigger的函数:
CREATE OR REPLACE FUNCTION check_reception()
RETURNS trigger AS $$
DECLARE valid boolean := false;
BEGIN
SELECT (SELECT u.reception FROM users u WHERE u.user_id = NEW.user_id)
= (SELECT t.reception FROM tariffs t WHERE t.tariff_id = NEW.tariff_id)
INTO valid FROM user_tariffs ;
IF valid = false
THEN RAISE EXCEPTION '(user, tariff, reception) invalid.';
END IF;
RETURN NEW;
END; $$ LANGUAGE plpgsql ;
并注册:
CREATE TRIGGER reception_trigger
AFTER INSERT OR UPDATE ON user_tariffs
FOR EACH ROW EXECUTE PROCEDURE check_reception();
现在尝试插入(1,2),这将是(便宜,昂贵),不允许:
INSERT INTO user_tariffs VALUES (1, 2);
ERROR: (user, tariff, reception) invalid.
KONTEXT: PL/pgSQL function check_reception() line 7 at RAISE
但我们可以插入(1,1),这是(便宜,便宜)没有问题:
INSERT INTO user_tariffs VALUES (1, 1);
SELECT * FROM user_tariffs;
<强>备注强>
在我看来,触发器不是最好的解决方案。尽可能避免触发器。他们可能有副作用(交易等)。检查StackOverflow以获取更多详细信息:)