स्पष्टीकरण
इस आवश्यकता . का सूत्रीकरण व्याख्या के लिए जगह छोड़ता है:
जहां UserRole.role_name
एक कर्मचारी भूमिका नाम शामिल है।
मेरी व्याख्या:UserRole
में एक प्रविष्टि के साथ जिसमें role_name = 'employee'
. है .
आपका नामकरण परंपरा <स्ट्राइक>हैस्ट्राइक> समस्याग्रस्त था (अब अपडेट किया गया)। User
मानक SQL और Postgres में एक आरक्षित शब्द है। यह पहचानकर्ता के रूप में अवैध है जब तक कि डबल-उद्धृत न हो - जो कि गलत होगा। उपयोगकर्ता कानूनी नाम ताकि आपको दोहरा-उद्धरण न करना पड़े।
मैं अपने कार्यान्वयन में परेशानी मुक्त पहचानकर्ताओं का उपयोग कर रहा हूं।
समस्या
FOREIGN KEY
और CHECK
संबंधपरक अखंडता को लागू करने के लिए बाधा सिद्ध, वायुरोधी उपकरण हैं। ट्रिगर शक्तिशाली, उपयोगी और बहुमुखी विशेषताएं हैं लेकिन अधिक परिष्कृत, कम सख्त और डिज़ाइन त्रुटियों और कोने के मामलों के लिए अधिक जगह के साथ हैं।
आपका मामला कठिन है क्योंकि एक FK बाधा पहली बार में असंभव लगती है:इसके लिए एक PRIMARY KEY
की आवश्यकता होती है या UNIQUE
संदर्भ के लिए बाधा - न तो NULL मानों की अनुमति देता है। कोई आंशिक एफके बाधाएं नहीं हैं, सख्त संदर्भात्मक अखंडता से केवल बचने के लिए संदर्भ में NULL मान हैं डिफ़ॉल्ट MATCH SIMPLE
. के कारण कॉलम एफके बाधाओं का व्यवहार। प्रति दस्तावेज़:
MATCH SIMPLE
किसी भी विदेशी कुंजी कॉलम को शून्य होने देता है; यदि उनमें से कोई भी शून्य है, तो संदर्भित तालिका में पंक्ति का मिलान होना आवश्यक नहीं है।
अधिक के साथ dba.SE पर संबंधित उत्तर:
- दो-स्तंभ विदेशी कुंजी बाधा केवल तभी जब तीसरा स्तंभ पूर्ण नहीं हो
वर्कअराउंड एक बूलियन ध्वज पेश करना है is_employee
दोनों पक्षों के कर्मचारियों को चिह्नित करने के लिए, परिभाषित NOT NULL
users
. में , लेकिन होने की अनुमति है NULL
user_role
. में :
समाधान
यह आपकी आवश्यकताओं को लागू करता है बिल्कुल , शोर और ओवरहेड को न्यूनतम रखते हुए:
CREATE TABLE users (
users_id serial PRIMARY KEY
, employee_nr int
, is_employee bool NOT NULL DEFAULT false
, CONSTRAINT role_employee CHECK (employee_nr IS NOT NULL = is_employee)
, UNIQUE (is_employee, users_id) -- required for FK (otherwise redundant)
);
CREATE TABLE user_role (
user_role_id serial PRIMARY KEY
, users_id int NOT NULL REFERENCES users
, role_name text NOT NULL
, is_employee bool CHECK(is_employee)
, CONSTRAINT role_employee
CHECK (role_name <> 'employee' OR is_employee IS TRUE)
, CONSTRAINT role_employee_requires_employee_nr_fk
FOREIGN KEY (is_employee, users_id) REFERENCES users(is_employee, users_id)
);
बस इतना ही।
ये ट्रिगर वैकल्पिक हैं लेकिन जोड़े गए टैग सेट करने की सुविधा के लिए अनुशंसित हैं is_employee
स्वचालित रूप से और आपको कुछ भी करने की आवश्यकता नहीं है अतिरिक्त:
-- users
CREATE OR REPLACE FUNCTION trg_users_insup_bef()
RETURNS trigger AS
$func$
BEGIN
NEW.is_employee = (NEW.employee_nr IS NOT NULL);
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF employee_nr ON users
FOR EACH ROW
EXECUTE PROCEDURE trg_users_insup_bef();
-- user_role
CREATE OR REPLACE FUNCTION trg_user_role_insup_bef()
RETURNS trigger AS
$func$
BEGIN
NEW.is_employee = true;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF role_name ON user_role
FOR EACH ROW
WHEN (NEW.role_name = 'employee')
EXECUTE PROCEDURE trg_user_role_insup_bef();
फिर से, कोई बकवास नहीं, अनुकूलित और केवल जरूरत पड़ने पर ही कॉल किया जाता है।
एसक्यूएल फिडल पोस्टग्रेज 9.3 के लिए डेमो। Postgres 9.1+ के साथ काम करना चाहिए।
प्रमुख बिंदु
-
अब, अगर हम
user_role.role_name = 'employee'
सेट करना चाहते हैं , तो एक मिलानuser.employee_nr
. होना चाहिए पहले। -
आप अब भी एक
employee_nr
जोड़ सकते हैं करने के लिए किसी उपयोगकर्ता, और आप (तब) अभी भी किसी भीuser_role
. को टैग कर सकते हैंis_employee
. के साथ , वास्तविकrole_name
. की परवाह किए बिना . यदि आपको आवश्यकता हो तो इसे अस्वीकार करना आसान है, लेकिन यह कार्यान्वयन आवश्यकता से अधिक प्रतिबंध नहीं लगाता है। -
users.is_employee
केवलtrue
हो सकता है याfalse
और एकemployee_nr
. के अस्तित्व को प्रतिबिंबित करने के लिए मजबूर किया जाता हैCHECK
. द्वारा बाधा ट्रिगर कॉलम को स्वचालित रूप से सिंक में रखता है। आपfalse
की अनुमति दे सकते हैं इसके अलावा अन्य उद्देश्यों के लिए डिज़ाइन में केवल मामूली अपडेट के साथ। -
user_role.is_employee
. के लिए नियम थोड़े अलग हैं:यह सच होना चाहिए अगरrole_name = 'employee'
. एकCHECK
द्वारा लागू किया गया बाधा और फिर से ट्रिगर द्वारा स्वचालित रूप से सेट करें। लेकिन इसेrole_name
बदलने की अनुमति है किसी और चीज़ के लिए और अभी भीis_employee
keep रखें .employee_nr
वाले उपयोगकर्ता को किसी ने नहीं कहा क्या आवश्यक हैuser_role
. में एक अनुरूप प्रविष्टि प्राप्त करने के लिए , बस दूसरी तरफ! फिर से, यदि आवश्यक हो तो अतिरिक्त रूप से लागू करना आसान है। -
यदि अन्य ट्रिगर हैं जो हस्तक्षेप कर सकते हैं, तो इस पर विचार करें:
PostgreSQL 9.2.1 में लूपिंग ट्रिगर कॉल से कैसे बचें
लेकिन हमें चिंता करने की ज़रूरत नहीं है कि नियमों का उल्लंघन हो सकता है क्योंकि उपरोक्त ट्रिगर केवल सुविधा के लिए हैं। प्रति नियमCHECK
. के साथ लागू होते हैं और FK बाधाएं, जो बिना किसी अपवाद के अनुमति देती हैं। -
इसके अलावा:मैंने कॉलम
is_employee
. रखा है सबसे पहले बाधा मेंUNIQUE (is_employee, users_id)
किसी कारण से .users_id
पहले से ही PK में शामिल है, इसलिए यह यहां दूसरा स्थान ले सकता है:
DB सहयोगी निकाय और अनुक्रमण