बहिष्करण प्रतिबंध
मेरा सुझाव है कि आप इसके बजाय एक बहिष्करण बाधा का उपयोग करें, जो बहुत आसान, सुरक्षित और तेज़ है:
आपको अतिरिक्त मॉड्यूल स्थापित करने की आवश्यकता है btree_gist
पहला। इस संबंधित उत्तर में निर्देश और स्पष्टीकरण देखें:
और आपको "ParentID"
. शामिल करना होगा तालिका में "Bar"
अनावश्यक रूप से, जो भुगतान करने के लिए एक छोटी सी कीमत होगी। तालिका परिभाषाएँ इस तरह दिख सकती हैं:
CREATE TABLE "Foo" (
"FooID" serial PRIMARY KEY
"ParentID" int4 NOT NULL REFERENCES "Parent"
"Details1" varchar
CONSTRAINT foo_parent_foo_uni UNIQUE ("ParentID", "FooID") -- required for FK
);
CREATE TABLE "Bar" (
"ParentID" int4 NOT NULL,
"FooID" int4 NOT NULL REFERENCES "Foo" ("FooID"),
"Timerange" tstzrange NOT NULL,
"Detail1" varchar,
"Detail2" varchar,
CONSTRAINT "Bar_pkey" PRIMARY KEY ("FooID", "Timerange"),
CONSTRAINT bar_foo_fk
FOREIGN KEY ("ParentID", "FooID") REFERENCES "Foo" ("ParentID", "FooID"),
CONSTRAINT bar_parent_timerange_excl
EXCLUDE USING gist ("ParentID" WITH =, "Timerange" WITH &&)
);
मैंने "Bar"."FooID"
. के लिए डेटा प्रकार भी बदल दिया है <स्ट्राइक>int8
. से स्ट्राइक> से int4
. यह "Foo"."FooID"
. का संदर्भ देता है , जो एक serial
. है , यानी int4
. मिलान प्रकार का प्रयोग करें int4
(या सिर्फ integer
) कई कारणों से, उनमें से एक प्रदर्शन है।
आपको अब किसी ट्रिगर की आवश्यकता नहीं है (कम से कम इस कार्य के लिए नहीं), और आप "Bar_FooID_Timerange_idx"
अनुक्रमणिका नहीं बनाते हैं स्ट्राइक> और भी, क्योंकि यह अपवर्जन बाधा द्वारा परोक्ष रूप से बनाया गया है।
("ParentID", "FooID")
. पर एक btree अनुक्रमणिका हालांकि सबसे अधिक उपयोगी होगा:
CREATE INDEX bar_parentid_fooid_idx ON "Bar" ("ParentID", "FooID");
संबंधित:
मैंने UNIQUE ("ParentID", "FooID")
. चुना है और इसके विपरीत किसी कारण से नहीं, क्योंकि एक अन्य अनुक्रमणिका है जिसमें प्रमुख "FooID"
है किसी भी तालिका में:
इसके अलावा:मैं कभी भी डबल-उद्धृत CaMeL का उपयोग नहीं करता -केस पहचानकर्ता पोस्टग्रेज में। मैं इसे केवल आपके लेआउट का अनुपालन करने के लिए यहां करता हूं।
अनावश्यक कॉलम से बचें
यदि आप "Bar"."ParentID"
. को शामिल नहीं कर सकते हैं या नहीं करेंगे तो अनावश्यक रूप से, एक और दुष्ट है रास्ता - इस शर्त पर कि "Foo"."ParentID"
कभी अपडेट नहीं किया गया . सुनिश्चित करें कि, उदाहरण के लिए एक ट्रिगर के साथ।
आप एक IMMUTABLE
को नकली बना सकते हैं समारोह:
CREATE OR REPLACE FUNCTION f_parent_of_foo(int)
RETURNS int AS
'SELECT "ParentID" FROM public."Foo" WHERE "FooID" = $1'
LANGUAGE sql IMMUTABLE;
मैंने public
. मानते हुए, तालिका नाम को सुनिश्चित करने के लिए स्कीमा-योग्यता प्राप्त की है . अपने स्कीमा के अनुकूल बनें।
अधिक:
- दूरस्थ रूप से संबंधित तालिका से मूल्यों की जांच करने के लिए बाधा (जुड़ने आदि के माध्यम से)
- क्या PostgreSQL "एक्सेंट असंवेदनशील" का समर्थन करता है "संयोजन?
फिर इसे बहिष्करण बाधा में उपयोग करें:
CONSTRAINT bar_parent_timerange_excl
EXCLUDE USING gist (f_parent_of_foo("FooID") WITH =, "Timerange" WITH &&)
एक अनावश्यक int4
saving को सहेजते समय कॉलम, बाधा को सत्यापित करना अधिक महंगा होगा और संपूर्ण समाधान अधिक पूर्व शर्त पर निर्भर करता है।
संघर्ष संभालें
आप INSERT
को लपेट सकते हैं और UPDATE
एक plpgsql फ़ंक्शन में और बहिष्करण बाधा से संभावित अपवादों को ट्रैप करें (23P01 exclusion_violation
) इसे किसी तरह से संभालने के लिए।
INSERT ...
EXCEPTION
WHEN exclusion_violation
THEN -- handle conflict
पूरा कोड उदाहरण:
पोस्टग्रेज 9.5 में विरोध को हैंडल करें
पोस्टग्रेज में 9.5 आप INSERT
handle को संभाल सकते हैं सीधे नए "यूपीएसईआरटी" कार्यान्वयन के साथ। दस्तावेज:
हालांकि:
लेकिन आप अभी भी ON CONFLICT DO NOTHING
. का उपयोग कर सकते हैं , इस प्रकार संभव exclusion_violation
. से बचना अपवाद बस जांचें कि क्या कोई पंक्ति वास्तव में अपडेट की गई थी, जो कि सस्ता है:
INSERT ...
ON CONFLICT ON CONSTRAINT bar_parent_timerange_excl DO NOTHING;
IF NOT FOUND THEN
-- handle conflict
END IF;
यह उदाहरण चेक को दी गई बहिष्करण बाधा तक सीमित करता है। (मैंने उपरोक्त तालिका परिभाषा में इस उद्देश्य के लिए स्पष्ट रूप से बाधा का नाम दिया है।) अन्य संभावित अपवाद पकड़े नहीं गए हैं।