2017-06-02 39 views
0

我有表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

接待就像contry。來自歐洲的人無法從美國獲得關稅。 –

+0

你有什麼功能依賴關係? '(用戶,接待) - >關稅,'(關稅,接待) - >用戶和'(用戶,資費) - >接待'?三個全部?任何子集?什麼是多對多?什麼是一對多?什麼是一對一?哪些是必須存在的(最少1項)?哪些可能有(零個或多個)?你的主鍵是什麼?什麼是獨特的?你的外鍵是什麼? – flutter

+0

User_id是唯一的,tariff_id是唯一的,所有的值都不爲空 –

回答

1

解決方案1:外鍵約束

我假設下面的表定義。 特別是,user_tariffs中的組合鍵使得它成爲userstariffs之間的多對多關係。

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進一步的細節:)

+0

我不明白它是如何防止濫用關稅不同的招待會,你能解釋一下嗎?好的,索引是獨一無二的,我們在users_tariffs中有獨特的'普京 - >廉價'的行,但廉價是不允許普京的接待關稅... –

+0

你可以添加一個示例行數據表嗎?只需幾行來進一步解釋問題。 – flutter

+0

示例在這個問題中 - 有一個E.G.塊。 –