解決方案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)
到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.
創建返回類型觸發器的功能:
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進一步的細節:)
接待就像contry。來自歐洲的人無法從美國獲得關稅。 –
你有什麼功能依賴關係? '(用戶,接待) - >關稅,'(關稅,接待) - >用戶和'(用戶,資費) - >接待'?三個全部?任何子集?什麼是多對多?什麼是一對多?什麼是一對一?哪些是必須存在的(最少1項)?哪些可能有(零個或多個)?你的主鍵是什麼?什麼是獨特的?你的外鍵是什麼? – flutter
User_id是唯一的,tariff_id是唯一的,所有的值都不爲空 –